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
@@ -0,0 +1,50 @@
package datadog.trace.instrumentation.vertx_4_0.server;

import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;

import com.google.auto.service.AutoService;
import datadog.trace.agent.tooling.Instrumenter;
import datadog.trace.agent.tooling.muzzle.Reference;

@AutoService(Instrumenter.class)
public class HttpServerResponseEndHandlerInstrumentation extends Instrumenter.Tracing
implements Instrumenter.ForKnownTypes {
public HttpServerResponseEndHandlerInstrumentation() {
super("vertx", "vertx-4.0");
}

@Override
public Reference[] additionalMuzzleReferences() {
return new Reference[] {VertxVersionMatcher.HTTP_1X_SERVER_RESPONSE};
}

@Override
public String[] helperClassNames() {
return new String[] {
packageName + ".EndHandlerWrapper",
packageName + ".RouteHandlerWrapper",
packageName + ".VertxDecorator",
packageName + ".VertxDecorator$VertxURIDataAdapter",
};
}

@Override
public String[] knownMatchingTypes() {
return new String[] {
"io.vertx.core.http.impl.Http1xServerResponse", "io.vertx.core.http.impl.Http2ServerResponse "
};
}

@Override
public void adviceTransformations(AdviceTransformation transformation) {
transformation.applyAdvice(
isMethod()
.and(named("endHandler"))
.and(isPublic())
.and(takesArgument(0, named("io.vertx.core.Handler"))),
packageName + ".EndHandlerWrapperAdvice");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package datadog.trace.instrumentation.vertx_4_0.server;

import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;

import com.google.auto.service.AutoService;
import datadog.trace.agent.tooling.Instrumenter;
import datadog.trace.agent.tooling.muzzle.Reference;

@AutoService(Instrumenter.class)
public class RouteHandlerInstrumentation extends Instrumenter.Tracing
implements Instrumenter.ForSingleType {
public RouteHandlerInstrumentation() {
super("vertx", "vertx-4.0");
}

@Override
public Reference[] additionalMuzzleReferences() {
return new Reference[] {VertxVersionMatcher.HTTP_1X_SERVER_RESPONSE};
}

@Override
public String[] helperClassNames() {
return new String[] {
packageName + ".EndHandlerWrapper",
packageName + ".RouteHandlerWrapper",
packageName + ".VertxDecorator",
packageName + ".VertxDecorator$VertxURIDataAdapter",
};
}

@Override
public String instrumentedType() {
return "io.vertx.ext.web.impl.RouteImpl";
}

@Override
public void adviceTransformations(AdviceTransformation transformation) {
transformation.applyAdvice(
isMethod()
.and(named("handler"))
.and(isPublic())
.and(takesArgument(0, named("io.vertx.core.Handler"))),
packageName + ".RouteHandlerWrapperAdvice");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package datadog.trace.instrumentation.vertx_4_0.server;

import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;

import com.google.auto.service.AutoService;
import datadog.trace.agent.tooling.Instrumenter;
import datadog.trace.agent.tooling.muzzle.Reference;

@AutoService(Instrumenter.class)
public class RouteImplInstrumentation extends Instrumenter.AppSec
implements Instrumenter.ForKnownTypes {

public RouteImplInstrumentation() {
super("vertx", "vertx-4.0");
}

@Override
public Reference[] additionalMuzzleReferences() {
return new Reference[] {VertxVersionMatcher.HTTP_1X_SERVER_RESPONSE};
}

@Override
public String[] knownMatchingTypes() {
return new String[] {
"io.vertx.ext.web.impl.RouteImpl", "io.vertx.ext.web.impl.RouteState",
};
}

@Override
public String[] helperClassNames() {
return new String[] {
packageName + ".PathParameterPublishingHelper",
};
}

@Override
public void adviceTransformations(AdviceTransformation transformation) {
transformation.applyAdvice(
named("matches")
.and(takesArguments(3))
.and(takesArgument(0, named("io.vertx.ext.web.impl.RoutingContextImplBase")))
.and(takesArgument(1, String.class))
.and(takesArgument(2, boolean.class))
.and(isPublic())
.and(returns(int.class)),
packageName + ".RouteMatchesAdvice");

transformation.applyAdvice(
named("matches")
.and(takesArguments(3))
.and(
takesArgument(
0,
named("io.vertx.ext.web.impl.RoutingContextImplBase")
.or(named("io.vertx.ext.web.RoutingContext"))))
.and(takesArgument(1, String.class))
.and(takesArgument(2, boolean.class))
.and(returns(boolean.class)),
packageName + ".RouteMatchesAdvice$BooleanReturnVariant");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package datadog.trace.instrumentation.vertx_4_0.server;

import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;

import com.google.auto.service.AutoService;
import datadog.trace.agent.tooling.Instrumenter;
import datadog.trace.agent.tooling.muzzle.Reference;

@AutoService(Instrumenter.class)
public class RoutingContextImplInstrumentation extends Instrumenter.AppSec
implements Instrumenter.ForSingleType {

public RoutingContextImplInstrumentation() {
super("vertx", "vertx-4.0");
}

@Override
public Reference[] additionalMuzzleReferences() {
return new Reference[] {VertxVersionMatcher.HTTP_1X_SERVER_RESPONSE};
}

@Override
public String instrumentedType() {
return "io.vertx.ext.web.impl.RoutingContextImpl";
}

@Override
public void adviceTransformations(AdviceTransformation transformation) {
transformation.applyAdvice(
named("getBodyAsJson")
.or(named("getBodyAsJsonArray"))
.and(takesArguments(1))
.and(takesArgument(0, int.class)),
packageName + ".RoutingContextJsonAdvice");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package datadog.trace.instrumentation.vertx_4_0.server;

import datadog.trace.agent.tooling.muzzle.Reference;

// checks for vertx > 4
public class VertxVersionMatcher {
// added in 4.0
static final Reference HTTP_1X_SERVER_RESPONSE =
new Reference.Builder("io.vertx.core.http.impl.Http1xServerResponse").build();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package datadog.trace.instrumentation.vertx_4_0.server;

import static datadog.trace.bootstrap.instrumentation.decorator.http.HttpResourceDecorator.HTTP_RESOURCE_DECORATOR;
import static datadog.trace.instrumentation.vertx_4_0.server.RouteHandlerWrapper.HANDLER_SPAN_CONTEXT_KEY;
import static datadog.trace.instrumentation.vertx_4_0.server.RouteHandlerWrapper.PARENT_SPAN_CONTEXT_KEY;
import static datadog.trace.instrumentation.vertx_4_0.server.RouteHandlerWrapper.ROUTE_CONTEXT_KEY;
import static datadog.trace.instrumentation.vertx_4_0.server.VertxDecorator.DECORATE;

import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;

public class EndHandlerWrapper implements Handler<Void> {
private final RoutingContext routingContext;

Handler<Void> actual;

EndHandlerWrapper(RoutingContext routingContext) {
this.routingContext = routingContext;
}

@Override
public void handle(final Void event) {
AgentSpan span = routingContext.get(HANDLER_SPAN_CONTEXT_KEY);
AgentSpan parentSpan = routingContext.get(PARENT_SPAN_CONTEXT_KEY);
String path = routingContext.get(ROUTE_CONTEXT_KEY);
try {
span.finishThreadMigration();
if (actual != null) {
actual.handle(event);
}
} finally {
if (path != null) {
HTTP_RESOURCE_DECORATOR.withRoute(
parentSpan, routingContext.request().method().name(), path, true);
}
DECORATE.onResponse(span, routingContext.response());
span.finish();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package datadog.trace.instrumentation.vertx_4_0.server;

import io.vertx.core.Handler;
import net.bytebuddy.asm.Advice;

public class EndHandlerWrapperAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void wrapHandler(
@Advice.FieldValue(value = "endHandler", readOnly = false) final Handler<Void> endHandler,
@Advice.Argument(value = 0, readOnly = false) Handler<Void> handler) {
// In case the handler instrumentation executes twice on the same response
if (endHandler instanceof EndHandlerWrapper && handler instanceof EndHandlerWrapper) {
return;
}
// If an end handler was already registered when our wrapper is registered, we save the one that
// existed before
if (handler instanceof EndHandlerWrapper && endHandler != null) {
((EndHandlerWrapper) handler).actual = endHandler;

// If the user registers an end handler and ours has already been registered then we wrap the
// users handler and swap the function argument for the wrapper
} else if (endHandler instanceof EndHandlerWrapper) {
((EndHandlerWrapper) endHandler).actual = handler;
handler = endHandler;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package datadog.trace.instrumentation.vertx_4_0.server;

import static datadog.trace.api.gateway.Events.EVENTS;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeSpan;

import datadog.trace.api.function.BiFunction;
import datadog.trace.api.gateway.CallbackProvider;
import datadog.trace.api.gateway.Flow;
import datadog.trace.api.gateway.RequestContext;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import datadog.trace.bootstrap.instrumentation.api.AgentTracer;
import java.util.Map;

public class PathParameterPublishingHelper {
public static void publishParams(Map<String, String> params) {
AgentSpan agentSpan = activeSpan();
if (agentSpan == null) {
return;
}

CallbackProvider cbp = AgentTracer.get().instrumentationGateway();
BiFunction<RequestContext<Object>, Map<String, ?>, Flow<Void>> callback =
cbp.getCallback(EVENTS.requestPathParams());
RequestContext<Object> requestContext = agentSpan.getRequestContext();
if (requestContext == null || callback == null) {
return;
}

callback.apply(requestContext, params);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package datadog.trace.instrumentation.vertx_4_0.server;

import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeSpan;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
import static datadog.trace.instrumentation.vertx_4_0.server.VertxDecorator.DECORATE;
import static datadog.trace.instrumentation.vertx_4_0.server.VertxDecorator.INSTRUMENTATION_NAME;

import datadog.trace.bootstrap.instrumentation.api.AgentScope;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import datadog.trace.bootstrap.instrumentation.api.Tags;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class RouteHandlerWrapper implements Handler<RoutingContext> {
private static final Logger log = LoggerFactory.getLogger(RouteHandlerWrapper.class);
static final String PARENT_SPAN_CONTEXT_KEY = AgentSpan.class.getName() + ".parent";
static final String HANDLER_SPAN_CONTEXT_KEY = AgentSpan.class.getName() + ".handler";
static final String ROUTE_CONTEXT_KEY = "dd." + Tags.HTTP_ROUTE;

private final Handler<RoutingContext> actual;

public RouteHandlerWrapper(final Handler<RoutingContext> handler) {
actual = handler;
}

@Override
public void handle(final RoutingContext routingContext) {
AgentSpan span = routingContext.get(HANDLER_SPAN_CONTEXT_KEY);
if (span == null) {
AgentSpan parentSpan = activeSpan();
routingContext.put(PARENT_SPAN_CONTEXT_KEY, parentSpan);

span = startSpan(INSTRUMENTATION_NAME);
routingContext.put(HANDLER_SPAN_CONTEXT_KEY, span);
// span is stored in the context and the span related work may proceed on any thread
span.startThreadMigration();

routingContext.response().endHandler(new EndHandlerWrapper(routingContext));
DECORATE.afterStart(span);
span.setResourceName(DECORATE.className(actual.getClass()));
} else {
// the span was retrieved from the context in 'suspended' state - need to be resumed
span.finishThreadMigration();
}

updateRoutingContextWithRoute(routingContext);

try (final AgentScope scope = activateSpan(span)) {
scope.setAsyncPropagation(true);
try {
actual.handle(routingContext);
} catch (final Throwable t) {
DECORATE.onError(span, t);
throw t;
}
}
}

private void updateRoutingContextWithRoute(RoutingContext routingContext) {
final String method = routingContext.request().method().name();
final String mountPoint = routingContext.mountPoint();

String path = routingContext.currentRoute().getPath();

if (mountPoint != null) {
final String noBackslashhMountPoint =
mountPoint.endsWith("/")
? mountPoint.substring(0, mountPoint.lastIndexOf("/"))
: mountPoint;
path = noBackslashhMountPoint + path;
}
if (method != null && path != null) {
routingContext.put(ROUTE_CONTEXT_KEY, path);
}
}
}
Loading