From 2cba37558e59df3c07d9ae48091f9ba710aeb211 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Tue, 5 Mar 2024 17:39:12 +0200 Subject: [PATCH 1/2] Make HTTP templates for observability work with subresources Fixes: #31497 --- .../ObservabilityCustomizer.java | 12 +++++++++-- .../observability/ObservabilityHandler.java | 21 ++++++++++++++++++- .../observability/ObservabilityUtil.java | 12 +++++++++-- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/observability/ObservabilityCustomizer.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/observability/ObservabilityCustomizer.java index f5c29691f2d41..34cd1a687d95b 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/observability/ObservabilityCustomizer.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/observability/ObservabilityCustomizer.java @@ -13,9 +13,17 @@ public class ObservabilityCustomizer implements HandlerChainCustomizer { public List handlers(Phase phase, ResourceClass resourceClass, ServerResourceMethod serverResourceMethod) { if (phase.equals(Phase.AFTER_MATCH)) { + String basePath = resourceClass.getPath(); + boolean isSubResource = basePath == null; ObservabilityHandler observabilityHandler = new ObservabilityHandler(); - observabilityHandler - .setTemplatePath(resourceClass.getPath() + serverResourceMethod.getPath()); + if (isSubResource) { + observabilityHandler.setTemplatePath(serverResourceMethod.getPath()); + observabilityHandler.setSubResource(true); + } else { + observabilityHandler.setTemplatePath(basePath + serverResourceMethod.getPath()); + observabilityHandler.setSubResource(false); + } + return Collections.singletonList(observabilityHandler); } return Collections.emptyList(); diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/observability/ObservabilityHandler.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/observability/ObservabilityHandler.java index a448289a89249..465b92992b712 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/observability/ObservabilityHandler.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/observability/ObservabilityHandler.java @@ -16,6 +16,8 @@ public class ObservabilityHandler implements ServerRestHandler { // make mutable to allow for bytecode serialization private String templatePath; + private boolean isSubResource; + public String getTemplatePath() { return templatePath; } @@ -24,9 +26,26 @@ public void setTemplatePath(String templatePath) { this.templatePath = MULTIPLE_SLASH_PATTERN.matcher(templatePath).replaceAll("/"); } + public boolean isSubResource() { + return isSubResource; + } + + public void setSubResource(boolean subResource) { + isSubResource = subResource; + } + @Override public void handle(ResteasyReactiveRequestContext requestContext) throws Exception { - setUrlPathTemplate(requestContext.unwrap(RoutingContext.class), templatePath); + RoutingContext routingContext = requestContext.unwrap(RoutingContext.class); + if (isSubResource) { + var previous = getUrlPathTemplate(routingContext); + if (previous == null) { + previous = ""; + } + setUrlPathTemplate(routingContext, previous + templatePath); + } else { + setUrlPathTemplate(routingContext, templatePath); + } } } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/observability/ObservabilityUtil.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/observability/ObservabilityUtil.java index 2576e8a7de7d4..5c5068e1734bc 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/observability/ObservabilityUtil.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/observability/ObservabilityUtil.java @@ -1,5 +1,6 @@ package io.quarkus.resteasy.reactive.server.runtime.observability; +import io.vertx.core.Context; import io.vertx.core.http.impl.HttpServerRequestInternal; import io.vertx.ext.web.RoutingContext; @@ -9,7 +10,14 @@ private ObservabilityUtil() { } static void setUrlPathTemplate(RoutingContext routingContext, String templatePath) { - ((HttpServerRequestInternal) (routingContext.request())).context() - .putLocal("UrlPathTemplate", templatePath); + getRequestContext(routingContext).putLocal("UrlPathTemplate", templatePath); + } + + static String getUrlPathTemplate(RoutingContext routingContext) { + return getRequestContext(routingContext).getLocal("UrlPathTemplate"); + } + + private static Context getRequestContext(RoutingContext routingContext) { + return ((HttpServerRequestInternal) (routingContext.request())).context(); } } From 69aa38297414500909160db93ae1dce588c938bd Mon Sep 17 00:00:00 2001 From: Thomas Darimont Date: Tue, 5 Mar 2024 20:04:15 +0100 Subject: [PATCH 2/2] Add tests for sub-resource parameter templating in prometheus metrics Signed-off-by: Thomas Darimont --- .../micrometer/prometheus/RootResource.java | 13 ++++++++++++ .../it/micrometer/prometheus/SubResource.java | 20 +++++++++++++++++++ .../PrometheusMetricsRegistryTest.java | 12 ++++++++++- 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 integration-tests/micrometer-prometheus/src/main/java/io/quarkus/it/micrometer/prometheus/RootResource.java create mode 100644 integration-tests/micrometer-prometheus/src/main/java/io/quarkus/it/micrometer/prometheus/SubResource.java diff --git a/integration-tests/micrometer-prometheus/src/main/java/io/quarkus/it/micrometer/prometheus/RootResource.java b/integration-tests/micrometer-prometheus/src/main/java/io/quarkus/it/micrometer/prometheus/RootResource.java new file mode 100644 index 0000000000000..bc62e4cae6164 --- /dev/null +++ b/integration-tests/micrometer-prometheus/src/main/java/io/quarkus/it/micrometer/prometheus/RootResource.java @@ -0,0 +1,13 @@ +package io.quarkus.it.micrometer.prometheus; + +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; + +@Path("/root") +public class RootResource { + + @Path("{rootParam}/sub") + public SubResource subResource(@PathParam("rootParam") String value) { + return new SubResource(value); + } +} diff --git a/integration-tests/micrometer-prometheus/src/main/java/io/quarkus/it/micrometer/prometheus/SubResource.java b/integration-tests/micrometer-prometheus/src/main/java/io/quarkus/it/micrometer/prometheus/SubResource.java new file mode 100644 index 0000000000000..ba5d61a5766b3 --- /dev/null +++ b/integration-tests/micrometer-prometheus/src/main/java/io/quarkus/it/micrometer/prometheus/SubResource.java @@ -0,0 +1,20 @@ +package io.quarkus.it.micrometer.prometheus; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; + +public class SubResource { + + private final String value; + + public SubResource(String value) { + this.value = value; + } + + @GET + @Path("/{subParam}") + public String get(@PathParam("subParam") String subParam) { + return value + ":" + subParam; + } +} 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 186cb2268a90b..1e530648096c2 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 @@ -98,6 +98,13 @@ void testSecuredEndpoint() { @Test @Order(11) + void testTemplatedPathOnSubResource() { + when().get("/root/r1/sub/s2").then().statusCode(200) + .body(containsString("r1:s2")); + } + + @Test + @Order(20) void testPrometheusScrapeEndpointTextPlain() { RestAssured.given().header("Accept", TextFormat.CONTENT_TYPE_004) .when().get("/q/metrics") @@ -133,6 +140,9 @@ void testPrometheusScrapeEndpointTextPlain() { .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}\"")) + .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}\"")) + // Verify Hibernate Metrics .body(containsString( "hibernate_sessions_open_total{entityManagerFactory=\"\",env=\"test\",env2=\"test\",registry=\"prometheus\",} 2.0")) @@ -195,7 +205,7 @@ void testPrometheusScrapeEndpointTextPlain() { } @Test - @Order(11) + @Order(20) void testPrometheusScrapeEndpointOpenMetrics() { RestAssured.given().header("Accept", TextFormat.CONTENT_TYPE_OPENMETRICS_100) .when().get("/q/metrics")