Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,14 @@ interface Context {
HttpServerRequest request();

HttpResponse response();

/**
* Gives access to the contextual data that was added while the HTTP request was active.
* This can be found of doing something like {@link io.smallrye.common.vertx.ContextLocals#get(String)},
* however this method is needed because {@link io.smallrye.common.vertx.ContextLocals#get(String)} won't
* work when {@link HttpServerMetricsTagsContributor#contribute(Context)} is called as the HTTP request has
* already gone away.
*/
<T> T requestContextLocalData(Object key);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import io.vertx.core.http.HttpServerOptions;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.http.ServerWebSocket;
import io.vertx.core.http.impl.HttpServerRequestInternal;
import io.vertx.core.spi.metrics.HttpServerMetrics;
import io.vertx.core.spi.observability.HttpRequest;
import io.vertx.core.spi.observability.HttpResponse;
Expand Down Expand Up @@ -269,5 +270,9 @@ public void disconnected(LongTaskTimer.Sample websocketMetric) {

private record DefaultContext(HttpServerRequest request,
HttpResponse response) implements HttpServerMetricsTagsContributor.Context {
@Override
public <T> T requestContextLocalData(Object key) {
return ((HttpServerRequestInternal) request).context().getLocal(key);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
import io.quarkus.micrometer.runtime.HttpServerMetricsTagsContributor;

@Singleton
public class DummyTag implements HttpServerMetricsTagsContributor {
public class ContextLocalTag implements HttpServerMetricsTagsContributor {

@Override
public Tags contribute(Context context) {
return Tags.of("dummy", "value");
String contextLocalData = context.requestContextLocalData("context-local");
return Tags.of("dummy", contextLocalData != null ? contextLocalData : "value");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;

import io.smallrye.common.vertx.ContextLocals;

@Path("template/path/{value}")
public class PathTemplateResource {
@GET
public String get(@PathParam("value") String value) {
ContextLocals.put("context-local", "val-" + value);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bad practice. We should not encourage ppl to add tags from path params. Even if they think the nr of different values are limited, they cannot really control it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be bad practice, but people shouldn't be limited by accidental technically reasons from doing it.

ThreadLocals should not be used generally, but that doesn't mean we should prevent their use.

Copy link
Contributor

@brunobat brunobat May 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair, but this should come with a big warning that they will shoot themselves in the foot if they don't consider cardinatility.
See for example: #users > Are you using path parameters? (metrics)

I think a warning should be placed in the docs and a in the test.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because in the end, ppl will complain and we will spend time debugging this stuff.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add we should also place a warning in the Javadoc

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to add something in the various places, I'll gladly approve

Copy link
Contributor

@adutra adutra May 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For context: my initial use case is not related to path params, it's related to capturing the tenant ID associated with the request. And tenant IDs are guaranteed to form a finite set, although they could be like a thousand or so. Are you saying that even this use case is bad practice?

Copy link
Contributor

@brunobat brunobat May 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. It must not be used like that.
It will blow the dimensions exponentially. Remember that for each tenant id you will have also different metrics per endpoint, response status and method.
Place that data in the span and later query the APM's span attributes and/or correlate with exemplars.

return "Received: " + value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,14 +132,14 @@ void testPrometheusScrapeEndpointTextPlain() {
.body(containsString("status=\"200\",uri=\"/secured/item/{id}\""))
.body(containsString("status=\"401\",uri=\"/secured/item/{id}\""))
.body(containsString("outcome=\"SUCCESS\""))
.body(containsString("dummy=\"value\""))
.body(containsString("dummy="))
.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\",foo_response=\"UNSET\",method=\"GET\",outcome=\"SUCCESS\",registry=\"prometheus\",status=\"200\",uri=\"/template/path/{value}\""))
"http_server_requests_seconds_count{dummy=\"val-anything\",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\",foo_response=\"UNSET\",method=\"GET\",outcome=\"SUCCESS\",registry=\"prometheus\",status=\"200\",uri=\"/root/{rootParam}/sub/{subParam}\""))
Expand Down Expand Up @@ -234,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\",foo_response=\"UNSET\",method=\"GET\",outcome=\"SUCCESS\",registry=\"prometheus\",status=\"200\",uri=\"/template/path/{value}\""))
"http_server_requests_seconds_count{dummy=\"val-anything\",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(
Expand Down
Loading