Example application to reproduce:
@SpringBootApplication
class DemoApplication
fun main(args: Array<String>) {
runApplication<DemoApplication>(*args)
}
@RestController
@RequestMapping("/")
class TestController {
@GetMapping
fun hello(): String {
return "Hello world"
}
}
Next we add actuator and prometheus dependencies to build:
plugins {
id("org.springframework.boot") version "3.0.2"
id("io.spring.dependency-management") version "1.1.0"
id("org.graalvm.buildtools.native") version "0.9.18"
kotlin("jvm") version "1.7.22"
kotlin("plugin.spring") version "1.7.22"
}
group = "com.example"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_17
repositories {
mavenCentral()
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-webflux")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("io.projectreactor.kotlin:reactor-kotlin-extensions")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("io.micrometer:micrometer-registry-prometheus")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("io.projectreactor:reactor-test")
}
tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "17"
}
}
tasks.withType<Test> {
useJUnitPlatform()
}
and enable percentiles reporting for incoming requests in application.yml
application.yml:
management:
metrics:
distribution:
percentiles:
http:
server:
requests: 0.98
endpoints:
web:
exposure:
include: prometheus
Run with: ./gradlew clean nativeRun
Hit http://localhost:8080/ and next http://localhost:8080/actuator/prometheus to get registered metrics values. Response is 500
An exception in logs can be found:
2023-02-15T13:18:42.546+02:00 ERROR 77491 --- [oundedElastic-1] a.w.r.e.AbstractErrorWebExceptionHandler : [22d47a73-3] 500 Server Error for HTTP GET "/actuator/prometheus"
java.lang.IllegalArgumentException: java.lang.NoSuchMethodException: org.HdrHistogram.ConcurrentHistogram.<init>(org.HdrHistogram.AbstractHistogram)
at org.HdrHistogram.DoubleHistogram.<init>(DoubleHistogram.java:220) ~[demo:2.1.12]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
*__checkpoint ⇢ Handler org.springframework.boot.actuate.endpoint.web.reactive.AbstractWebFluxEndpointHandlerMapping$ReadOperationHandler#handle(ServerWebExchange) [DispatcherHandler]
*__checkpoint ⇢ org.springframework.web.filter.reactive.ServerHttpObservationFilter [DefaultWebFilterChain]
*__checkpoint ⇢ HTTP GET "/actuator/prometheus" [ExceptionHandlingWebHandler]
Original Stack Trace:
at org.HdrHistogram.DoubleHistogram.<init>(DoubleHistogram.java:220) ~[demo:2.1.12]
at org.HdrHistogram.DoubleHistogram.<init>(DoubleHistogram.java:230) ~[demo:2.1.12]
at org.HdrHistogram.ConcurrentDoubleHistogram.<init>(ConcurrentDoubleHistogram.java:92) ~[demo:2.1.12]
at org.HdrHistogram.DoubleRecorder$InternalConcurrentDoubleHistogram.<init>(DoubleRecorder.java:322) ~[na:na]
at org.HdrHistogram.DoubleRecorder$InternalConcurrentDoubleHistogram.<init>(DoubleRecorder.java:306) ~[na:na]
at org.HdrHistogram.DoubleRecorder.performIntervalSample(DoubleRecorder.java:275) ~[demo:2.1.12]
at org.HdrHistogram.DoubleRecorder.getIntervalHistogramInto(DoubleRecorder.java:254) ~[demo:2.1.12]
at io.micrometer.core.instrument.distribution.TimeWindowPercentileHistogram.accumulate(TimeWindowPercentileHistogram.java:73) ~[na:na]
at io.micrometer.core.instrument.distribution.AbstractTimeWindowHistogram.accumulateIfStale(AbstractTimeWindowHistogram.java:181) ~[demo:1.10.3]
at io.micrometer.core.instrument.distribution.AbstractTimeWindowHistogram.takeSnapshot(AbstractTimeWindowHistogram.java:171) ~[demo:1.10.3]
at io.micrometer.core.instrument.AbstractTimer.takeSnapshot(AbstractTimer.java:247) ~[demo:1.10.3]
at io.micrometer.prometheus.PrometheusTimer.takeSnapshot(PrometheusTimer.java:137) ~[na:na]
at io.micrometer.prometheus.PrometheusMeterRegistry.lambda$addDistributionStatisticSamples$16(PrometheusMeterRegistry.java:448) ~[demo:1.10.3]
at io.micrometer.prometheus.MicrometerCollector.collect(MicrometerCollector.java:75) ~[na:na]
at io.prometheus.client.Collector.collect(Collector.java:45) ~[demo:na]
at io.prometheus.client.CollectorRegistry$MetricFamilySamplesEnumeration.findNextElement(CollectorRegistry.java:204) ~[na:na]
at io.prometheus.client.CollectorRegistry$MetricFamilySamplesEnumeration.nextElement(CollectorRegistry.java:219) ~[na:na]
at io.prometheus.client.CollectorRegistry$MetricFamilySamplesEnumeration.nextElement(CollectorRegistry.java:152) ~[na:na]
at io.prometheus.client.exporter.common.TextFormat.write004(TextFormat.java:71) ~[na:na]
at org.springframework.boot.actuate.metrics.export.prometheus.TextOutputFormat$1.write(TextOutputFormat.java:45) ~[na:na]
at org.springframework.boot.actuate.metrics.export.prometheus.PrometheusScrapeEndpoint.scrape(PrometheusScrapeEndpoint.java:62) ~[demo:3.0.2]
at java.base@17.0.6/java.lang.reflect.Method.invoke(Method.java:568) ~[demo:na]
at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:281) ~[na:na]
at org.springframework.boot.actuate.endpoint.invoke.reflect.ReflectiveOperationInvoker.invoke(ReflectiveOperationInvoker.java:74) ~[na:na]
at org.springframework.boot.actuate.endpoint.annotation.AbstractDiscoveredOperation.invoke(AbstractDiscoveredOperation.java:60) ~[demo:3.0.2]
at org.springframework.boot.actuate.endpoint.web.reactive.AbstractWebFluxEndpointHandlerMapping$ElasticSchedulerInvoker.lambda$invoke$0(AbstractWebFluxEndpointHandlerMapping.java:245) ~[na:na]
It works when it's run as JVM application with ./gradlew clean bootRun.
Comment From: wilkinsona
Thanks for the report.
This needs to be addressed with some improvements to the reachability metadata for org.hdrhistrogram:HdrHistogram library to allow reflective calls to the org.HdrHistogram.ConcurrentHistogram.ConcurrentHistogram(AbstractHistogram) constructor. Please open a reachability metadata issue and comment here with a link to it.
In the meantime, you can provide some reflection config in your app by adding to src/main/resources a META-INF/native-image/reflect-config.json with the following contents:
[
{
"name": "org.HdrHistogram.ConcurrentHistogram",
"methods": [
{
"name": "<init>",
"parameterTypes": [
"org.HdrHistogram.AbstractHistogram"
]
}
]
}
]
Comment From: gzeskas
Created a ticket in graalvm reacabilty: https://github.com/oracle/graalvm-reachability-metadata/issues/234
Comment From: wilkinsona
Thanks, @gzeskas.