Skip to content

Commit

Permalink
Micrometer Vert.x HTTP request propagation
Browse files Browse the repository at this point in the history
  • Loading branch information
ebullient authored and gsmet committed Oct 1, 2020
1 parent 4ed971c commit 3b317d6
Show file tree
Hide file tree
Showing 8 changed files with 143 additions and 129 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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<String, Object> {
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> T getFromRoutingContext(String key) {
if (routingContext != null) {
return routingContext.get(key);
}
return null;
}

<T> T getValue(String key) {
Object o = this.get(key);
return o == null ? null : (T) o;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -22,13 +24,13 @@
/**
* HttpServerMetrics<R, W, S>
* <ul>
* <li>R for Request metric -- MetricsContext</li>
* <li>R for Request metric -- RequestMetricContext</li>
* <li>W for Websocket metric -- LongTaskTimer sample</li>
* <li>S for Socket metric -- MetricsContext</li>
* <li>S for Socket metric -- Map<String, Object></li>
* </ul>
*/
public class VertxHttpServerMetrics extends VertxTcpMetrics
implements HttpServerMetrics<MetricsContext, LongTaskTimer.Sample, MetricsContext> {
implements HttpServerMetrics<RequestMetric, LongTaskTimer.Sample, Map<String, Object>> {
static final Logger log = Logger.getLogger(VertxHttpServerMetrics.class);

final List<Pattern> ignorePatterns;
Expand Down Expand Up @@ -68,97 +70,110 @@ 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<String, Object> 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;
}

/**
* Called when an http server request begins. Vert.x will invoke
* {@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<String, Object> 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<String, Object> 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)))
Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<RoutingContext> {
private static final Logger log = Logger.getLogger(VertxMeterFilter.class);

Expand All @@ -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<Void>() {
@Override
public void handle(Void x) {
MetricsContext.removeRoutingContext(context);
}
});
RequestMetric requestMetric = RequestMetric.retrieveRequestMetric(context);
if (requestMetric != null) {
requestMetric.routingContext = event;
}

event.next();
}
}
Loading

0 comments on commit 3b317d6

Please sign in to comment.