From 3b317d674563d4d163e03895a6999ccee30c8a6c Mon Sep 17 00:00:00 2001 From: Erin Schnabel Date: Thu, 17 Sep 2020 21:24:45 -0400 Subject: [PATCH] Micrometer Vert.x HTTP request propagation --- .../binder/VertxBinderProcessor.java | 2 +- .../runtime/binder/vertx/MetricsContext.java | 56 ---------- .../runtime/binder/vertx/RequestMetric.java | 56 ++++++++++ .../binder/vertx/VertxHttpServerMetrics.java | 100 ++++++++++-------- .../VertxMeterBinderContainerFilter.java | 2 +- .../binder/vertx/VertxMeterFilter.java | 18 ++-- .../binder/vertx/VertxNetworkMetrics.java | 10 +- .../runtime/binder/vertx/VertxTcpMetrics.java | 28 +++-- 8 files changed, 143 insertions(+), 129 deletions(-) delete mode 100644 extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/MetricsContext.java create mode 100644 extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/RequestMetric.java diff --git a/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/binder/VertxBinderProcessor.java b/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/binder/VertxBinderProcessor.java index f5c97e4f2e7f9..1b83795ff4188 100644 --- a/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/binder/VertxBinderProcessor.java +++ b/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/binder/VertxBinderProcessor.java @@ -47,7 +47,7 @@ ResteasyJaxrsProviderBuildItem createVertxFilters() { @BuildStep(onlyIf = VertxBinderEnabled.class) FilterBuildItem addVertxMeterFilter() { - return new FilterBuildItem(new VertxMeterFilter(), 10); + return new FilterBuildItem(new VertxMeterFilter(), Integer.MAX_VALUE); } @BuildStep(onlyIf = VertxBinderEnabled.class) diff --git a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/MetricsContext.java b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/MetricsContext.java deleted file mode 100644 index 4c0016cac26a9..0000000000000 --- a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/MetricsContext.java +++ /dev/null @@ -1,56 +0,0 @@ -package io.quarkus.micrometer.runtime.binder.vertx; - -import java.util.HashMap; - -import io.vertx.core.Context; -import io.vertx.ext.web.RoutingContext; - -public class MetricsContext extends HashMap { - static final String METRICS_CONTEXT = "METRICS_CONTEXT"; - - static final String HTTP_REQUEST_PATH = "HTTP_REQUEST_PATH"; - static final String HTTP_REQUEST_SAMPLE = "HTTP_REQUEST_SAMPLE"; - - final Context vertxContext; - volatile RoutingContext routingContext; - - static MetricsContext addMetricsContext(Context vertxContext) { - MetricsContext ctx = new MetricsContext(vertxContext); - vertxContext.put(METRICS_CONTEXT, ctx); - return ctx; - } - - private MetricsContext(Context vertxContext) { - this.vertxContext = vertxContext; - } - - public void removeMetricsContext() { - vertxContext.remove(METRICS_CONTEXT); - } - - T getFromRoutingContext(String key) { - if (routingContext != null) { - return routingContext.get(key); - } - return null; - } - - T getValue(String key) { - Object o = this.get(key); - return o == null ? null : (T) o; - } - - public static void addRoutingContext(Context context, RoutingContext event) { - MetricsContext metricsContext = context.get(METRICS_CONTEXT); - if (metricsContext != null) { - metricsContext.routingContext = event; - } - } - - public static void removeRoutingContext(Context context) { - MetricsContext metricsContext = context.get(METRICS_CONTEXT); - if (metricsContext != null) { - metricsContext.routingContext = null; - } - } -} diff --git a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/RequestMetric.java b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/RequestMetric.java new file mode 100644 index 0000000000000..ae796184e075d --- /dev/null +++ b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/RequestMetric.java @@ -0,0 +1,56 @@ +package io.quarkus.micrometer.runtime.binder.vertx; + +import java.util.HashMap; + +import io.vertx.core.Context; +import io.vertx.ext.web.RoutingContext; + +public class RequestMetric extends HashMap { + static final String METRICS_CONTEXT = "HTTP_REQUEST_METRICS_CONTEXT"; + + static final String HTTP_REQUEST_PATH = "HTTP_REQUEST_PATH"; + static final String HTTP_REQUEST_SAMPLE = "HTTP_REQUEST_SAMPLE"; + + volatile RoutingContext routingContext; + + /** + * Stash the RequestMetric in the Vertx Context + * + * @param context Vertx context to store RequestMetric in + * @param requestMetric + * @see VertxMeterFilter + */ + public static void setRequestMetric(Context context, RequestMetric requestMetric) { + if (context != null) { + context.put(METRICS_CONTEXT, requestMetric); + } + } + + /** + * Retrieve and remove the RequestMetric from the Vertx Context + * + * @param context + * @return the RequestMetricContext stored in the Vertx Context, or null + * @see VertxMeterFilter + */ + public static RequestMetric retrieveRequestMetric(Context context) { + if (context != null) { + RequestMetric requestMetric = context.get(METRICS_CONTEXT); + context.remove(METRICS_CONTEXT); + return requestMetric; + } + return null; + } + + T getFromRoutingContext(String key) { + if (routingContext != null) { + return routingContext.get(key); + } + return null; + } + + T getValue(String key) { + Object o = this.get(key); + return o == null ? null : (T) o; + } +} 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 23794a79039ba..197be5f5cf971 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 @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.regex.Pattern; import org.jboss.logging.Logger; @@ -13,6 +14,7 @@ import io.micrometer.core.instrument.Timer; import io.micrometer.core.instrument.binder.http.Outcome; import io.quarkus.micrometer.runtime.config.runtime.VertxConfig; +import io.vertx.core.Vertx; import io.vertx.core.http.HttpMethod; import io.vertx.core.http.HttpServerRequest; import io.vertx.core.http.HttpServerResponse; @@ -22,13 +24,13 @@ /** * HttpServerMetrics *
    - *
  • R for Request metric -- MetricsContext
  • + *
  • R for Request metric -- RequestMetricContext
  • *
  • W for Websocket metric -- LongTaskTimer sample
  • - *
  • S for Socket metric -- MetricsContext
  • + *
  • S for Socket metric -- Map
  • *
*/ public class VertxHttpServerMetrics extends VertxTcpMetrics - implements HttpServerMetrics { + implements HttpServerMetrics> { static final Logger log = Logger.getLogger(VertxHttpServerMetrics.class); final List ignorePatterns; @@ -68,23 +70,27 @@ public class VertxHttpServerMetrics extends VertxTcpMetrics /** * Called when an http server response is pushed. * - * @param socketMetric a MetricsContext object for socket metric context + * @param socketMetric a Map for socket metric context or null * @param method the pushed response method * @param uri the pushed response uri * @param response the http server response - * @return a MetricsContext object for request metric context or null + * @return a RequestMetricContext */ @Override - public MetricsContext responsePushed(MetricsContext socketMetric, HttpMethod method, String uri, + public RequestMetric responsePushed(Map socketMetric, HttpMethod method, String uri, HttpServerResponse response) { + RequestMetric requestMetric = new RequestMetric(); String path = VertxMetricsTags.parseUriPath(matchPatterns, ignorePatterns, uri); if (path != null) { - registry.counter(nameHttpServerPush, - Tags.of(VertxMetricsTags.uri(path, response.getStatusCode()), VertxMetricsTags.method(method), - VertxMetricsTags.outcome(response), VertxMetricsTags.status(response.getStatusCode()))) + registry.counter(nameHttpServerPush, Tags.of( + VertxMetricsTags.uri(path, response.getStatusCode()), + VertxMetricsTags.method(method), + VertxMetricsTags.outcome(response), + VertxMetricsTags.status(response.getStatusCode()))) .increment(); } - return socketMetric; + log.debugf("responsePushed %s: %s, %s", uri, socketMetric, requestMetric); + return requestMetric; } /** @@ -92,73 +98,82 @@ public MetricsContext responsePushed(MetricsContext socketMetric, HttpMethod met * {@link #responseEnd} when the response has ended or {@link #requestReset} if * the request/response has failed before. * - * @param socketMetric a MetricsContext object for socket metric context + * @param socketMetric a Map for socket metric context or null * @param request the http server request - * @return a MetricsContext object for request metric context or null + * @return a RequestMetricContext */ @Override - public MetricsContext requestBegin(MetricsContext socketMetric, HttpServerRequest request) { + public RequestMetric requestBegin(Map socketMetric, HttpServerRequest request) { + RequestMetric requestMetric = new RequestMetric(); + RequestMetric.setRequestMetric(Vertx.currentContext(), requestMetric); + String path = VertxMetricsTags.parseUriPath(matchPatterns, ignorePatterns, request.uri()); - if (path != null && socketMetric != null) { + if (path != null) { // Pre-add the request method tag to the sample - socketMetric.put(MetricsContext.HTTP_REQUEST_SAMPLE, + requestMetric.put(RequestMetric.HTTP_REQUEST_SAMPLE, Timer.start(registry).tags(Tags.of(VertxMetricsTags.method(request.method())))); // remember the path to monitor for use later (maybe a 404 or redirect..) - socketMetric.put(MetricsContext.HTTP_REQUEST_PATH, path); + requestMetric.put(RequestMetric.HTTP_REQUEST_PATH, path); } - return socketMetric; + + log.debugf("requestBegin %s: %s, %s, %s", request.uri(), socketMetric, requestMetric, request.headers()); + return requestMetric; } /** * Called when the http server request couldn't complete successfully, for * instance the connection was closed before the response was sent. * - * @param requestMetric a MetricsContext object for request metric context or null + * @param requestMetric a RequestMetricContext or null */ @Override - public void requestReset(MetricsContext requestMetric) { + public void requestReset(RequestMetric requestMetric) { + log.debugf("requestReset: %s", requestMetric); Timer.Sample sample = getRequestSample(requestMetric); if (sample != null) { String requestPath = getServerRequestPath(requestMetric); sample.stop(registry, - Timer.builder(nameHttpServerRequests) - .tags(Tags.of(VertxMetricsTags.uri(requestPath, 0), - Outcome.CLIENT_ERROR.asTag(), VertxMetricsTags.STATUS_RESET))); + Timer.builder(nameHttpServerRequests).tags(Tags.of( + VertxMetricsTags.uri(requestPath, 0), + Outcome.CLIENT_ERROR.asTag(), + VertxMetricsTags.STATUS_RESET))); } } /** * Called when an http server response has ended. * - * @param requestMetric a MetricsContext object for request metric context or null + * @param requestMetric a RequestMetricContext or null * @param response the http server response */ @Override - public void responseEnd(MetricsContext requestMetric, HttpServerResponse response) { + public void responseEnd(RequestMetric requestMetric, HttpServerResponse response) { + log.debugf("responseEnd: %s, %s", requestMetric, response); + Timer.Sample sample = getRequestSample(requestMetric); if (sample != null) { String requestPath = getServerRequestPath(requestMetric); - sample.stop(registry, Timer.builder(nameHttpServerRequests) - .tags(Tags.of( - VertxMetricsTags.uri(requestPath, response.getStatusCode()), - VertxMetricsTags.outcome(response), - VertxMetricsTags.status(response.getStatusCode())))); + sample.stop(registry, Timer.builder(nameHttpServerRequests).tags(Tags.of( + VertxMetricsTags.uri(requestPath, response.getStatusCode()), + VertxMetricsTags.outcome(response), + VertxMetricsTags.status(response.getStatusCode())))); } } /** * Called when a server web socket connects. * - * @param socketMetric a MetricsContext object for socket metric context or null - * @param requestMetric a MetricsContext object for request metric context or null + * @param socketMetric a Map for socket metric context or null + * @param requestMetric a RequestMetricContext or null * @param serverWebSocket the server web socket - * @return a LongTaskTimer.Sample containing websocket metric context or null + * @return a LongTaskTimer.Sample or null */ @Override - public LongTaskTimer.Sample connected(MetricsContext socketMetric, MetricsContext requestMetric, + public LongTaskTimer.Sample connected(Map socketMetric, RequestMetric requestMetric, ServerWebSocket serverWebSocket) { - String path = getServerRequestPath(socketMetric); + log.debugf("websocket connected: %s, %s, %s", socketMetric, requestMetric, serverWebSocket); + String path = getServerRequestPath(requestMetric); if (path != null) { return LongTaskTimer.builder(nameWebsocketConnections) .tags(Tags.of(VertxMetricsTags.uri(path, 0))) @@ -171,32 +186,33 @@ public LongTaskTimer.Sample connected(MetricsContext socketMetric, MetricsContex /** * Called when the server web socket has disconnected. * - * @param websocketMetric a LongTaskTimer.Sample containing websocket metric context or null + * @param websocketMetric a LongTaskTimer.Sample or null */ @Override public void disconnected(LongTaskTimer.Sample websocketMetric) { + log.debugf("websocket disconnected: %s", websocketMetric); if (websocketMetric != null) { websocketMetric.stop(); } } - private Timer.Sample getRequestSample(MetricsContext requestMetric) { - if (requestMetric == null) { + private Timer.Sample getRequestSample(RequestMetric metricsContext) { + if (metricsContext == null) { return null; } - return requestMetric.getValue(MetricsContext.HTTP_REQUEST_SAMPLE); + return metricsContext.getValue(RequestMetric.HTTP_REQUEST_SAMPLE); } - private String getServerRequestPath(MetricsContext source) { - if (source == null) { + private String getServerRequestPath(RequestMetric metricsContext) { + if (metricsContext == null) { return null; } - String path = source.getFromRoutingContext(MetricsContext.HTTP_REQUEST_PATH); + String path = metricsContext.getFromRoutingContext(RequestMetric.HTTP_REQUEST_PATH); if (path != null) { log.debugf("Using path from routing context %s", path); return path; } - return source.getValue(MetricsContext.HTTP_REQUEST_PATH); + return metricsContext.getValue(RequestMetric.HTTP_REQUEST_PATH); } } diff --git a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxMeterBinderContainerFilter.java b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxMeterBinderContainerFilter.java index 3a9f8bfa4f77e..2048248398322 100644 --- a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxMeterBinderContainerFilter.java +++ b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxMeterBinderContainerFilter.java @@ -33,7 +33,7 @@ public void filter(final ContainerRequestContext requestContext) { } log.debugf("Saving parameterized path %s in %s", path, routingContext); - routingContext.put(MetricsContext.HTTP_REQUEST_PATH, path); + routingContext.put(RequestMetric.HTTP_REQUEST_PATH, path); } } } diff --git a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxMeterFilter.java b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxMeterFilter.java index a916e295b2165..f72d539ded17f 100644 --- a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxMeterFilter.java +++ b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxMeterFilter.java @@ -7,6 +7,12 @@ import io.vertx.core.Vertx; import io.vertx.ext.web.RoutingContext; +/** + * High priority handler that sets the Vertx RouterContext + * attribute on the active RequestMetric. + * To quote Stuart, "YUCK". + * Reference: https://github.com/eclipse-vertx/vert.x/issues/3579 + */ public class VertxMeterFilter implements Handler { private static final Logger log = Logger.getLogger(VertxMeterFilter.class); @@ -15,16 +21,10 @@ public void handle(RoutingContext event) { final Context context = Vertx.currentContext(); log.debugf("Handling event %s with context %s", event, context); - if (context != null) { - MetricsContext.addRoutingContext(context, event); - event.addBodyEndHandler(new Handler() { - @Override - public void handle(Void x) { - MetricsContext.removeRoutingContext(context); - } - }); + RequestMetric requestMetric = RequestMetric.retrieveRequestMetric(context); + if (requestMetric != null) { + requestMetric.routingContext = event; } - event.next(); } } diff --git a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxNetworkMetrics.java b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxNetworkMetrics.java index 20637f3da5224..93208bbe26cbe 100644 --- a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxNetworkMetrics.java +++ b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxNetworkMetrics.java @@ -1,5 +1,7 @@ package io.quarkus.micrometer.runtime.binder.vertx; +import java.util.Map; + import io.micrometer.core.instrument.DistributionSummary; import io.micrometer.core.instrument.MeterRegistry; import io.vertx.core.net.SocketAddress; @@ -11,7 +13,7 @@ *
  • S for Socket metric -- Vert.x Context
  • * */ -public class VertxNetworkMetrics implements NetworkMetrics { +public class VertxNetworkMetrics implements NetworkMetrics> { final MeterRegistry registry; final String nameBytesRead; @@ -33,7 +35,7 @@ public class VertxNetworkMetrics implements NetworkMetrics { * @param numberOfBytes the number of bytes read */ @Override - public void bytesRead(MetricsContext socketMetric, SocketAddress remoteAddress, long numberOfBytes) { + public void bytesRead(Map socketMetric, SocketAddress remoteAddress, long numberOfBytes) { DistributionSummary.builder(nameBytesRead).register(registry).record(numberOfBytes); } @@ -45,7 +47,7 @@ public void bytesRead(MetricsContext socketMetric, SocketAddress remoteAddress, * @param numberOfBytes the number of bytes written */ @Override - public void bytesWritten(MetricsContext socketMetric, SocketAddress remoteAddress, long numberOfBytes) { + public void bytesWritten(Map socketMetric, SocketAddress remoteAddress, long numberOfBytes) { DistributionSummary.builder(nameBytesWritten).register(registry).record(numberOfBytes); } @@ -58,7 +60,7 @@ public void bytesWritten(MetricsContext socketMetric, SocketAddress remoteAddres * @param t the exception that occurred */ @Override - public void exceptionOccurred(MetricsContext socketMetric, SocketAddress remoteAddress, Throwable t) { + public void exceptionOccurred(Map socketMetric, SocketAddress remoteAddress, Throwable t) { registry.counter(nameExceptionOccurred, "class", t.getClass().getName()).increment(); } } diff --git a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxTcpMetrics.java b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxTcpMetrics.java index 594b47f5294ed..b9bf30a35b56f 100644 --- a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxTcpMetrics.java +++ b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxTcpMetrics.java @@ -1,14 +1,16 @@ package io.quarkus.micrometer.runtime.binder.vertx; +import java.util.HashMap; +import java.util.Map; + import io.micrometer.core.instrument.LongTaskTimer; import io.micrometer.core.instrument.MeterRegistry; -import io.vertx.core.Context; -import io.vertx.core.Vertx; import io.vertx.core.net.SocketAddress; import io.vertx.core.spi.metrics.TCPMetrics; public class VertxTcpMetrics extends VertxNetworkMetrics - implements TCPMetrics { + implements TCPMetrics> { + private static final String CONNECTED_SOCKET_SAMPLE = "CONNECTED_SOCKET_SAMPLE"; final String nameConnections; @@ -27,30 +29,25 @@ public class VertxTcpMetrics extends VertxNetworkMetrics * * @param remoteAddress the remote address of the client * @param remoteName the remote name of the client - * @return a MetricsContext object for socket metric context or null + * @return a map for socket metric context */ @Override - public MetricsContext connected(SocketAddress remoteAddress, String remoteName) { - Context vertxContext = Vertx.currentContext(); - if (vertxContext == null) { - return null; - } - MetricsContext metricsContext = MetricsContext.addMetricsContext(vertxContext); - - metricsContext.put(CONNECTED_SOCKET_SAMPLE, + public Map connected(SocketAddress remoteAddress, String remoteName) { + Map socketMetric = new HashMap<>(); + socketMetric.put(CONNECTED_SOCKET_SAMPLE, LongTaskTimer.builder(nameConnections).register(registry).start()); - return metricsContext; + return socketMetric; } /** * Called when a client has disconnected, which is applicable for TCP * connections. * - * @param socketMetric a MetricsContext object for socket metric context or null + * @param socketMetric a Map for socket metric context or null * @param remoteAddress the remote address of the client */ @Override - public void disconnected(MetricsContext socketMetric, SocketAddress remoteAddress) { + public void disconnected(Map socketMetric, SocketAddress remoteAddress) { if (socketMetric == null) { return; } @@ -58,6 +55,5 @@ public void disconnected(MetricsContext socketMetric, SocketAddress remoteAddres if (sample != null) { sample.stop(); } - socketMetric.removeMetricsContext(); } }