diff --git a/ktor-server/ktor-server-plugins/ktor-server-metrics-micrometer/jvm/src/io/ktor/server/metrics/micrometer/MicrometerMetrics.kt b/ktor-server/ktor-server-plugins/ktor-server-metrics-micrometer/jvm/src/io/ktor/server/metrics/micrometer/MicrometerMetrics.kt index 47981f2c2ae..12760acb1e4 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-metrics-micrometer/jvm/src/io/ktor/server/metrics/micrometer/MicrometerMetrics.kt +++ b/ktor-server/ktor-server-plugins/ktor-server-metrics-micrometer/jvm/src/io/ktor/server/metrics/micrometer/MicrometerMetrics.kt @@ -4,6 +4,7 @@ package io.ktor.server.metrics.micrometer +import io.ktor.http.HttpMethod.Companion.DefaultMethods import io.ktor.server.application.* import io.ktor.server.application.hooks.* import io.ktor.server.application.hooks.Metrics @@ -146,19 +147,22 @@ public val MicrometerMetrics: ApplicationPlugin = @OptIn(InternalAPI::class) on(Metrics) { call -> - active?.incrementAndGet() - call.attributes.put(measureKey, CallMeasure(Timer.start(registry))) + if (call.request.httpMethod in DefaultMethods) { + active?.incrementAndGet() + call.attributes.put(measureKey, CallMeasure(Timer.start(registry))) + } } on(ResponseSent) { call -> - active?.decrementAndGet() - val measure = call.attributes[measureKey] - measure.timer.stop( - Timer.builder(metricName) - .addDefaultTags(call, measure.throwable) - .apply { pluginConfig.timerBuilder(this, call, measure.throwable) } - .register(registry) - ) + call.attributes.getOrNull(measureKey)?.let { measure -> + active?.decrementAndGet() + measure.timer.stop( + Timer.builder(metricName) + .addDefaultTags(call, measure.throwable) + .apply { pluginConfig.timerBuilder(this, call, measure.throwable) } + .register(registry) + ) + } } on(CallFailed) { call, cause -> @@ -167,7 +171,9 @@ public val MicrometerMetrics: ApplicationPlugin = } application.monitor.subscribe(RoutingRoot.RoutingCallStarted) { call -> - call.attributes[measureKey].route = call.route.parent.toString() + call.attributes.getOrNull(measureKey)?.let { measure -> + measure.route = call.route.parent.toString() + } } } diff --git a/ktor-server/ktor-server-plugins/ktor-server-metrics-micrometer/jvm/test/io/ktor/server/metrics/micrometer/MicrometerMetricsTests.kt b/ktor-server/ktor-server-plugins/ktor-server-metrics-micrometer/jvm/test/io/ktor/server/metrics/micrometer/MicrometerMetricsTests.kt index 737d75c2e74..30b3bc091a4 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-metrics-micrometer/jvm/test/io/ktor/server/metrics/micrometer/MicrometerMetricsTests.kt +++ b/ktor-server/ktor-server-plugins/ktor-server-metrics-micrometer/jvm/test/io/ktor/server/metrics/micrometer/MicrometerMetricsTests.kt @@ -67,6 +67,37 @@ class MicrometerMetricsTests { testRegistry.assertActive(0.0) } + @Test + fun `time is not measured for custom http requests`() = testApplication { + val testRegistry = SimpleMeterRegistry() + install(MicrometerMetrics) { + registry = testRegistry + } + + routing { + get("/uri") { + call.respond("hello") + } + } + + var timers = testRegistry.find(requestTimeTimerName).timers() + assertTrue(timers.isEmpty()) + + client.get("/uri") + timers = testRegistry.find(requestTimeTimerName).timers() + assertEquals(1, timers.size, "Metrics should be recorded for GET requests") + + client.request("/uri") { + method = HttpMethod("CUSTOM") + } + timers = testRegistry.find(requestTimeTimerName).timers() + assertEquals(1, timers.size, "Metrics should not be recorded for custom requests") + + client.put("/uri") + timers = testRegistry.find(requestTimeTimerName).timers() + assertEquals(2, timers.size, "Metrics should be recorded for PUT requests") + } + @Test fun `errors are recorded`() = testApplication { val testRegistry = SimpleMeterRegistry()