From 9ac63d5259dd6de27ce615746b9c49969e5ecf80 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Tue, 21 Jan 2025 12:07:09 +0200 Subject: [PATCH 1/2] Add HTTP response to HttpServerMetricsTagsContributor.Context This is useful for creating tags based on HTTP response headers for example Closes: #45717 --- .../main/asciidoc/telemetry-micrometer.adoc | 4 +++- .../HttpServerMetricsTagsContributor.java | 3 +++ .../binder/vertx/VertxHttpServerMetrics.java | 5 +++-- .../java/io/quarkus/ResponseHeaderTag.java | 20 +++++++++++++++++++ .../micrometer/prometheus/FruitResource.java | 8 +++++++- .../micrometer/prometheus/ExemplarTest.java | 2 +- .../PrometheusMetricsRegistryTest.java | 7 ++++--- 7 files changed, 41 insertions(+), 8 deletions(-) create mode 100644 integration-tests/micrometer-prometheus/src/main/java/io/quarkus/ResponseHeaderTag.java diff --git a/docs/src/main/asciidoc/telemetry-micrometer.adoc b/docs/src/main/asciidoc/telemetry-micrometer.adoc index 2e4b60fb1095b..f9f9414ea0cb1 100644 --- a/docs/src/main/asciidoc/telemetry-micrometer.adoc +++ b/docs/src/main/asciidoc/telemetry-micrometer.adoc @@ -588,7 +588,9 @@ link:https://micrometer.io/docs/concepts[official documentation]. === Use `HttpServerMetricsTagsContributor` for server HTTP requests -By providing CDI beans that implement `io.quarkus.micrometer.runtime.HttpServerMetricsTagsContributor`, user code can contribute arbitrary tags based on the details of HTTP request +By providing CDI beans that implement `io.quarkus.micrometer.runtime.HttpServerMetricsTagsContributor`, user code can contribute arbitrary tags based on the details of HTTP request and responses. + +CAUTION: When creating tags using this interface, it's important to limit the cardinality of the values, otherwise there is a risk of severely degrading the metrics system's capacity. === Use `HttpClientMetricsTagsContributor` for client HTTP requests diff --git a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/HttpServerMetricsTagsContributor.java b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/HttpServerMetricsTagsContributor.java index 88651e171b3d8..0dec6727f65f9 100644 --- a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/HttpServerMetricsTagsContributor.java +++ b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/HttpServerMetricsTagsContributor.java @@ -3,6 +3,7 @@ import io.micrometer.core.instrument.Tags; import io.micrometer.core.instrument.config.MeterFilter; import io.vertx.core.http.HttpServerRequest; +import io.vertx.core.spi.observability.HttpResponse; /** * Allows code to add additional Micrometer {@link Tags} to the metrics collected for completed HTTP server requests. @@ -20,5 +21,7 @@ public interface HttpServerMetricsTagsContributor { interface Context { HttpServerRequest request(); + + HttpResponse response(); } } diff --git a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxHttpServerMetrics.java b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxHttpServerMetrics.java index e7148767cc0ee..8714275850068 100644 --- a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxHttpServerMetrics.java +++ b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxHttpServerMetrics.java @@ -205,7 +205,7 @@ public void responseEnd(HttpRequestMetric requestMetric, HttpResponse response, VertxMetricsTags.outcome(response), HttpCommonTags.status(response.statusCode())); if (!httpServerMetricsTagsContributors.isEmpty()) { - HttpServerMetricsTagsContributor.Context context = new DefaultContext(requestMetric.request()); + HttpServerMetricsTagsContributor.Context context = new DefaultContext(requestMetric.request(), response); for (int i = 0; i < httpServerMetricsTagsContributors.size(); i++) { try { Tags additionalTags = httpServerMetricsTagsContributors.get(i).contribute(context); @@ -258,6 +258,7 @@ public void disconnected(LongTaskTimer.Sample websocketMetric) { } } - private record DefaultContext(HttpServerRequest request) implements HttpServerMetricsTagsContributor.Context { + private record DefaultContext(HttpServerRequest request, + HttpResponse response) implements HttpServerMetricsTagsContributor.Context { } } diff --git a/integration-tests/micrometer-prometheus/src/main/java/io/quarkus/ResponseHeaderTag.java b/integration-tests/micrometer-prometheus/src/main/java/io/quarkus/ResponseHeaderTag.java new file mode 100644 index 0000000000000..2339df1a30809 --- /dev/null +++ b/integration-tests/micrometer-prometheus/src/main/java/io/quarkus/ResponseHeaderTag.java @@ -0,0 +1,20 @@ +package io.quarkus; + +import jakarta.inject.Singleton; + +import io.micrometer.core.instrument.Tags; +import io.quarkus.micrometer.runtime.HttpServerMetricsTagsContributor; + +@Singleton +public class ResponseHeaderTag implements HttpServerMetricsTagsContributor { + + @Override + public Tags contribute(Context context) { + var headerValue = context.response().headers().get("foo-response"); + String value = "UNSET"; + if ((headerValue != null) && !headerValue.isEmpty()) { + value = headerValue; + } + return Tags.of("foo-response", value); + } +} diff --git a/integration-tests/micrometer-prometheus/src/main/java/io/quarkus/it/micrometer/prometheus/FruitResource.java b/integration-tests/micrometer-prometheus/src/main/java/io/quarkus/it/micrometer/prometheus/FruitResource.java index 4227515888079..34c9fa5416b59 100644 --- a/integration-tests/micrometer-prometheus/src/main/java/io/quarkus/it/micrometer/prometheus/FruitResource.java +++ b/integration-tests/micrometer-prometheus/src/main/java/io/quarkus/it/micrometer/prometheus/FruitResource.java @@ -6,6 +6,8 @@ import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; +import org.jboss.resteasy.reactive.RestResponse; + import io.quarkus.hibernate.orm.panache.PanacheQuery; import io.smallrye.common.annotation.Blocking; @@ -26,9 +28,13 @@ public void trigger() { @GET @Path("all") - public void retrieveAll() { + public RestResponse retrieveAll() { PanacheQuery query = Fruit.find( "select name from Fruit"); List all = query.list(); + + return RestResponse.ResponseBuilder.noContent() + .header("foo-response", "value") + .build(); } } diff --git a/integration-tests/micrometer-prometheus/src/test/java/io/quarkus/it/micrometer/prometheus/ExemplarTest.java b/integration-tests/micrometer-prometheus/src/test/java/io/quarkus/it/micrometer/prometheus/ExemplarTest.java index 50e94d6220ae0..4bd040ac8725a 100644 --- a/integration-tests/micrometer-prometheus/src/test/java/io/quarkus/it/micrometer/prometheus/ExemplarTest.java +++ b/integration-tests/micrometer-prometheus/src/test/java/io/quarkus/it/micrometer/prometheus/ExemplarTest.java @@ -24,7 +24,7 @@ void testExemplar() { when().get("/example/prime/7919").then().statusCode(200); String metricMatch = "http_server_requests_seconds_count{dummy=\"value\",env=\"test\"," + - "env2=\"test\",foo=\"UNSET\",method=\"GET\",outcome=\"SUCCESS\"," + + "env2=\"test\",foo=\"UNSET\",foo_response=\"UNSET\",method=\"GET\",outcome=\"SUCCESS\"," + "registry=\"prometheus\",status=\"200\",uri=\"/example/prime/{number}\"} 2.0 # {span_id=\""; await().atMost(5, SECONDS).untilAsserted(() -> { diff --git a/integration-tests/micrometer-prometheus/src/test/java/io/quarkus/it/micrometer/prometheus/PrometheusMetricsRegistryTest.java b/integration-tests/micrometer-prometheus/src/test/java/io/quarkus/it/micrometer/prometheus/PrometheusMetricsRegistryTest.java index 1e530648096c2..5ee1d84b3927d 100644 --- a/integration-tests/micrometer-prometheus/src/test/java/io/quarkus/it/micrometer/prometheus/PrometheusMetricsRegistryTest.java +++ b/integration-tests/micrometer-prometheus/src/test/java/io/quarkus/it/micrometer/prometheus/PrometheusMetricsRegistryTest.java @@ -134,14 +134,15 @@ void testPrometheusScrapeEndpointTextPlain() { .body(containsString("outcome=\"SUCCESS\"")) .body(containsString("dummy=\"value\"")) .body(containsString("foo=\"bar\"")) + .body(containsString("foo_response=\"value\"")) .body(containsString("uri=\"/message/match/{id}/{sub}\"")) .body(containsString("uri=\"/message/match/{other}\"")) .body(containsString( - "http_server_requests_seconds_count{dummy=\"value\",env=\"test\",env2=\"test\",foo=\"UNSET\",method=\"GET\",outcome=\"SUCCESS\",registry=\"prometheus\",status=\"200\",uri=\"/template/path/{value}\"")) + "http_server_requests_seconds_count{dummy=\"value\",env=\"test\",env2=\"test\",foo=\"UNSET\",foo_response=\"UNSET\",method=\"GET\",outcome=\"SUCCESS\",registry=\"prometheus\",status=\"200\",uri=\"/template/path/{value}\"")) .body(containsString( - "http_server_requests_seconds_count{dummy=\"value\",env=\"test\",env2=\"test\",foo=\"UNSET\",method=\"GET\",outcome=\"SUCCESS\",registry=\"prometheus\",status=\"200\",uri=\"/root/{rootParam}/sub/{subParam}\"")) + "http_server_requests_seconds_count{dummy=\"value\",env=\"test\",env2=\"test\",foo=\"UNSET\",foo_response=\"UNSET\",method=\"GET\",outcome=\"SUCCESS\",registry=\"prometheus\",status=\"200\",uri=\"/root/{rootParam}/sub/{subParam}\"")) // Verify Hibernate Metrics .body(containsString( @@ -233,7 +234,7 @@ void testPrometheusScrapeEndpointOpenMetrics() { .body(containsString("uri=\"/message/match/{other}\"")) .body(containsString( - "http_server_requests_seconds_count{dummy=\"value\",env=\"test\",env2=\"test\",foo=\"UNSET\",method=\"GET\",outcome=\"SUCCESS\",registry=\"prometheus\",status=\"200\",uri=\"/template/path/{value}\"")) + "http_server_requests_seconds_count{dummy=\"value\",env=\"test\",env2=\"test\",foo=\"UNSET\",foo_response=\"UNSET\",method=\"GET\",outcome=\"SUCCESS\",registry=\"prometheus\",status=\"200\",uri=\"/template/path/{value}\"")) // Verify Hibernate Metrics .body(containsString( From bd0b5e18e7132ff2052c554f29f5ed86dd523cba Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Tue, 21 Jan 2025 13:02:53 +0200 Subject: [PATCH 2/2] Add HTTP response to HttpClientMetricsTagsContributor.Context --- .../micrometer/runtime/HttpClientMetricsTagsContributor.java | 3 +++ .../runtime/binder/vertx/VertxHttpClientMetrics.java | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/HttpClientMetricsTagsContributor.java b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/HttpClientMetricsTagsContributor.java index 54fc08a5ffe1b..c6c01ba80f54f 100644 --- a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/HttpClientMetricsTagsContributor.java +++ b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/HttpClientMetricsTagsContributor.java @@ -3,6 +3,7 @@ import io.micrometer.core.instrument.Tags; import io.micrometer.core.instrument.config.MeterFilter; import io.vertx.core.spi.observability.HttpRequest; +import io.vertx.core.spi.observability.HttpResponse; /** * Allows code to add additional Micrometer {@link Tags} to the metrics collected for completed HTTP client requests. @@ -20,5 +21,7 @@ public interface HttpClientMetricsTagsContributor { interface Context { HttpRequest request(); + + HttpResponse response(); } } diff --git a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxHttpClientMetrics.java b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxHttpClientMetrics.java index 34cc38ef6878b..159d3598f1c6d 100644 --- a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxHttpClientMetrics.java +++ b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxHttpClientMetrics.java @@ -172,7 +172,7 @@ public void responseEnd(RequestTracker tracker, long bytesRead) { .and(HttpCommonTags.status(tracker.response.statusCode())) .and(HttpCommonTags.outcome(tracker.response.statusCode())); if (!httpClientMetricsTagsContributors.isEmpty()) { - HttpClientMetricsTagsContributor.Context context = new DefaultContext(tracker.request); + HttpClientMetricsTagsContributor.Context context = new DefaultContext(tracker.request, tracker.response); for (int i = 0; i < httpClientMetricsTagsContributors.size(); i++) { try { Tags additionalTags = httpClientMetricsTagsContributors.get(i).contribute(context); @@ -254,6 +254,7 @@ public String getNormalizedUriPath(Map serverMatchPatterns, Lis } } - private record DefaultContext(HttpRequest request) implements HttpClientMetricsTagsContributor.Context { + private record DefaultContext(HttpRequest request, + HttpResponse response) implements HttpClientMetricsTagsContributor.Context { } }