Skip to content

Commit

Permalink
Introduce a way to provide custom micrometer metrics tags
Browse files Browse the repository at this point in the history
  • Loading branch information
geoand committed Jul 3, 2023
1 parent 5d31f63 commit 64ea8c1
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
import jakarta.interceptor.Interceptor;

import io.quarkus.arc.deployment.SyntheticBeansRuntimeInitBuildItem;
import io.quarkus.arc.deployment.UnremovableBeanBuildItem;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.BuildSteps;
import io.quarkus.deployment.annotations.Consume;
import io.quarkus.deployment.annotations.ExecutionTime;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.micrometer.runtime.HttpServerMetricsTagsContributor;
import io.quarkus.micrometer.runtime.MicrometerRecorder;
import io.quarkus.micrometer.runtime.binder.vertx.VertxMeterBinderRecorder;
import io.quarkus.micrometer.runtime.config.MicrometerConfig;
Expand All @@ -34,6 +36,11 @@ public boolean getAsBoolean() {
}
}

@BuildStep
UnremovableBeanBuildItem unremoveableAdditionalHttpServerMetrics() {
return UnremovableBeanBuildItem.beanTypes(HttpServerMetricsTagsContributor.class);
}

@BuildStep
@Record(value = ExecutionTime.STATIC_INIT)
VertxOptionsConsumerBuildItem build(VertxMeterBinderRecorder recorder) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.quarkus.micrometer.runtime;

import io.micrometer.core.instrument.Tags;
import io.micrometer.core.instrument.config.MeterFilter;
import io.vertx.core.http.HttpServerRequest;

/**
* Allows code to add additional Micrometer {@link Tags} to the metrics collected for completed HTTP server requests.
* <p>
* The implementations of this interface are meant to be registered via CDI beans.
*
* @see MeterFilter for a more advanced and feature complete way of interacting with {@link Tags}
*/
public interface HttpServerMetricsTagsContributor {

/**
* Called when Vert.x http server response has ended
*/
Tags contribute(Context context);

interface Context {
HttpServerRequest request();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package io.quarkus.micrometer.runtime.binder.vertx;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.LongAdder;

import org.jboss.logging.Logger;
Expand All @@ -10,9 +13,13 @@
import io.micrometer.core.instrument.Tags;
import io.micrometer.core.instrument.Timer;
import io.micrometer.core.instrument.binder.http.Outcome;
import io.quarkus.arc.Arc;
import io.quarkus.arc.ArcContainer;
import io.quarkus.micrometer.runtime.HttpServerMetricsTagsContributor;
import io.quarkus.micrometer.runtime.binder.HttpBinderConfiguration;
import io.quarkus.micrometer.runtime.binder.HttpCommonTags;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.http.ServerWebSocket;
import io.vertx.core.spi.metrics.HttpServerMetrics;
import io.vertx.core.spi.observability.HttpRequest;
Expand All @@ -37,6 +44,8 @@ public class VertxHttpServerMetrics extends VertxTcpServerMetrics
final String nameHttpServerRequests;
final LongAdder activeRequests;

private final List<HttpServerMetricsTagsContributor> httpServerMetricsTagsContributors;

VertxHttpServerMetrics(MeterRegistry registry, HttpBinderConfiguration config) {
super(registry, "http.server", null);
this.config = config;
Expand All @@ -49,6 +58,27 @@ public class VertxHttpServerMetrics extends VertxTcpServerMetrics
activeRequests = new LongAdder();
Gauge.builder(config.getHttpServerActiveRequestsName(), activeRequests, LongAdder::doubleValue)
.register(registry);

httpServerMetricsTagsContributors = resolveHttpServerMetricsTagsContributors();
}

private List<HttpServerMetricsTagsContributor> resolveHttpServerMetricsTagsContributors() {
final List<HttpServerMetricsTagsContributor> httpServerMetricsTagsContributors;
ArcContainer arcContainer = Arc.container();
if (arcContainer == null) {
httpServerMetricsTagsContributors = Collections.emptyList();
} else {
var handles = arcContainer.listAll(HttpServerMetricsTagsContributor.class);
if (handles.isEmpty()) {
httpServerMetricsTagsContributors = Collections.emptyList();
} else {
httpServerMetricsTagsContributors = new ArrayList<>(handles.size());
for (var handle : handles) {
httpServerMetricsTagsContributors.add(handle.get());
}
}
}
return httpServerMetricsTagsContributors;
}

/**
Expand Down Expand Up @@ -148,12 +178,23 @@ public void responseEnd(HttpRequestMetric requestMetric, HttpResponse response,
config.getServerIgnorePatterns());
if (path != null) {
Timer.Sample sample = requestMetric.getSample();
Timer.Builder builder = Timer.builder(nameHttpServerRequests)
.tags(Tags.of(
VertxMetricsTags.method(requestMetric.request().method()),
HttpCommonTags.uri(path, response.statusCode()),
VertxMetricsTags.outcome(response),
HttpCommonTags.status(response.statusCode())));
Tags allTags = Tags.of(
VertxMetricsTags.method(requestMetric.request().method()),
HttpCommonTags.uri(path, response.statusCode()),
VertxMetricsTags.outcome(response),
HttpCommonTags.status(response.statusCode()));
if (!httpServerMetricsTagsContributors.isEmpty()) {
HttpServerMetricsTagsContributor.Context context = new DefaultContext(requestMetric.request());
for (int i = 0; i < httpServerMetricsTagsContributors.size(); i++) {
try {
Tags additionalTags = httpServerMetricsTagsContributors.get(i).contribute(context);
allTags = allTags.and(additionalTags);
} catch (Exception e) {
log.debug("Unable to obtain additional tags", e);
}
}
}
Timer.Builder builder = Timer.builder(nameHttpServerRequests).tags(allTags);

sample.stop(builder.register(registry));
}
Expand Down Expand Up @@ -195,4 +236,17 @@ public void disconnected(LongTaskTimer.Sample websocketMetric) {
websocketMetric.stop();
}
}

private static class DefaultContext implements HttpServerMetricsTagsContributor.Context {
private final HttpServerRequest request;

private DefaultContext(HttpServerRequest request) {
this.request = request;
}

@Override
public HttpServerRequest request() {
return request;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.quarkus;

import jakarta.inject.Singleton;

import io.micrometer.core.instrument.Tags;
import io.quarkus.micrometer.runtime.HttpServerMetricsTagsContributor;

@Singleton
public class DummyTag implements HttpServerMetricsTagsContributor {

@Override
public Tags contribute(Context context) {
return Tags.of("dummy", "value");
}
}
Original file line number Diff line number Diff line change
@@ -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 HeaderTag implements HttpServerMetricsTagsContributor {

@Override
public Tags contribute(Context context) {
String headerValue = context.request().getHeader("Foo");
String value = "UNSET";
if ((headerValue != null) && !headerValue.isEmpty()) {
value = headerValue;
}
return Tags.of("foo", value);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.quarkus.it.micrometer.prometheus;

import static io.restassured.RestAssured.given;
import static io.restassured.RestAssured.when;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
Expand Down Expand Up @@ -44,7 +45,7 @@ void testServerError() {
@Test
@Order(4)
void testPathParameter() {
when().get("/message/item/123").then().statusCode(200);
given().header("foo", "bar").when().get("/message/item/123").then().statusCode(200);
}

@Test
Expand Down Expand Up @@ -111,11 +112,13 @@ void testPrometheusScrapeEndpointTextPlain() {
.body(containsString("uri=\"/message\""))
.body(containsString("uri=\"/message/item/{id}\""))
.body(containsString("outcome=\"SUCCESS\""))
.body(containsString("dummy=\"value\""))
.body(containsString("foo=\"bar\""))
.body(containsString("uri=\"/message/match/{id}/{sub}\""))
.body(containsString("uri=\"/message/match/{other}\""))

.body(containsString(
"http_server_requests_seconds_count{env=\"test\",method=\"GET\",outcome=\"SUCCESS\",registry=\"prometheus\",status=\"200\",uri=\"/template/path/{value}\""))
"http_server_requests_seconds_count{dummy=\"value\",env=\"test\",foo=\"UNSET\",method=\"GET\",outcome=\"SUCCESS\",registry=\"prometheus\",status=\"200\",uri=\"/template/path/{value}\""))

// Verify Hibernate Metrics
.body(containsString(
Expand Down Expand Up @@ -207,7 +210,7 @@ void testPrometheusScrapeEndpointOpenMetrics() {
.body(containsString("uri=\"/message/match/{other}\""))

.body(containsString(
"http_server_requests_seconds_count{env=\"test\",method=\"GET\",outcome=\"SUCCESS\",registry=\"prometheus\",status=\"200\",uri=\"/template/path/{value}\""))
"http_server_requests_seconds_count{dummy=\"value\",env=\"test\",foo=\"UNSET\",method=\"GET\",outcome=\"SUCCESS\",registry=\"prometheus\",status=\"200\",uri=\"/template/path/{value}\""))

// Verify Hibernate Metrics
.body(containsString(
Expand Down

0 comments on commit 64ea8c1

Please sign in to comment.