diff --git a/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java/datadog/trace/instrumentation/vertx_4_0/server/HttpServerResponseEndHandlerInstrumentation.java b/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java/datadog/trace/instrumentation/vertx_4_0/server/HttpServerResponseEndHandlerInstrumentation.java new file mode 100644 index 00000000000..4d962806b46 --- /dev/null +++ b/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java/datadog/trace/instrumentation/vertx_4_0/server/HttpServerResponseEndHandlerInstrumentation.java @@ -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"); + } +} diff --git a/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java/datadog/trace/instrumentation/vertx_4_0/server/RouteHandlerInstrumentation.java b/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java/datadog/trace/instrumentation/vertx_4_0/server/RouteHandlerInstrumentation.java new file mode 100644 index 00000000000..d77ee5c4e32 --- /dev/null +++ b/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java/datadog/trace/instrumentation/vertx_4_0/server/RouteHandlerInstrumentation.java @@ -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"); + } +} diff --git a/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java/datadog/trace/instrumentation/vertx_4_0/server/RouteImplInstrumentation.java b/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java/datadog/trace/instrumentation/vertx_4_0/server/RouteImplInstrumentation.java new file mode 100644 index 00000000000..68c9fd484e5 --- /dev/null +++ b/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java/datadog/trace/instrumentation/vertx_4_0/server/RouteImplInstrumentation.java @@ -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"); + } +} diff --git a/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java/datadog/trace/instrumentation/vertx_4_0/server/RoutingContextImplInstrumentation.java b/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java/datadog/trace/instrumentation/vertx_4_0/server/RoutingContextImplInstrumentation.java new file mode 100644 index 00000000000..379c523dd7a --- /dev/null +++ b/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java/datadog/trace/instrumentation/vertx_4_0/server/RoutingContextImplInstrumentation.java @@ -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"); + } +} diff --git a/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java/datadog/trace/instrumentation/vertx_4_0/server/VertxVersionMatcher.java b/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java/datadog/trace/instrumentation/vertx_4_0/server/VertxVersionMatcher.java new file mode 100644 index 00000000000..76a78a0e649 --- /dev/null +++ b/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java/datadog/trace/instrumentation/vertx_4_0/server/VertxVersionMatcher.java @@ -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(); +} diff --git a/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java8/datadog/trace/instrumentation/vertx_4_0/server/EndHandlerWrapper.java b/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java8/datadog/trace/instrumentation/vertx_4_0/server/EndHandlerWrapper.java new file mode 100644 index 00000000000..d85e3a57b86 --- /dev/null +++ b/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java8/datadog/trace/instrumentation/vertx_4_0/server/EndHandlerWrapper.java @@ -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 { + private final RoutingContext routingContext; + + Handler 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(); + } + } +} diff --git a/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java8/datadog/trace/instrumentation/vertx_4_0/server/EndHandlerWrapperAdvice.java b/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java8/datadog/trace/instrumentation/vertx_4_0/server/EndHandlerWrapperAdvice.java new file mode 100644 index 00000000000..bcfa635e631 --- /dev/null +++ b/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java8/datadog/trace/instrumentation/vertx_4_0/server/EndHandlerWrapperAdvice.java @@ -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 endHandler, + @Advice.Argument(value = 0, readOnly = false) Handler 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; + } + } +} diff --git a/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java8/datadog/trace/instrumentation/vertx_4_0/server/PathParameterPublishingHelper.java b/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java8/datadog/trace/instrumentation/vertx_4_0/server/PathParameterPublishingHelper.java new file mode 100644 index 00000000000..2a9004010cb --- /dev/null +++ b/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java8/datadog/trace/instrumentation/vertx_4_0/server/PathParameterPublishingHelper.java @@ -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 params) { + AgentSpan agentSpan = activeSpan(); + if (agentSpan == null) { + return; + } + + CallbackProvider cbp = AgentTracer.get().instrumentationGateway(); + BiFunction, Map, Flow> callback = + cbp.getCallback(EVENTS.requestPathParams()); + RequestContext requestContext = agentSpan.getRequestContext(); + if (requestContext == null || callback == null) { + return; + } + + callback.apply(requestContext, params); + } +} diff --git a/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java8/datadog/trace/instrumentation/vertx_4_0/server/RouteHandlerWrapper.java b/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java8/datadog/trace/instrumentation/vertx_4_0/server/RouteHandlerWrapper.java new file mode 100644 index 00000000000..5e99789d0f3 --- /dev/null +++ b/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java8/datadog/trace/instrumentation/vertx_4_0/server/RouteHandlerWrapper.java @@ -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 { + 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 actual; + + public RouteHandlerWrapper(final Handler 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); + } + } +} diff --git a/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java8/datadog/trace/instrumentation/vertx_4_0/server/RouteHandlerWrapperAdvice.java b/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java8/datadog/trace/instrumentation/vertx_4_0/server/RouteHandlerWrapperAdvice.java new file mode 100644 index 00000000000..9b537eec547 --- /dev/null +++ b/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java8/datadog/trace/instrumentation/vertx_4_0/server/RouteHandlerWrapperAdvice.java @@ -0,0 +1,20 @@ +package datadog.trace.instrumentation.vertx_4_0.server; + +import io.vertx.core.Handler; +import io.vertx.ext.web.RoutingContext; +import io.vertx.ext.web.impl.RouteImpl; +import net.bytebuddy.asm.Advice; + +public class RouteHandlerWrapperAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void wrapHandler( + @Advice.Argument(value = 0, readOnly = false) Handler handler) { + // When mounting a sub router, the handler is a method reference to the routers handleContext + // method this skips that. This prevents routers from creating a span during handling. In the + // event a route is not found, without this code, a span would be created for the router when + // it shouldn't + if (!handler.getClass().getName().startsWith(RouteImpl.class.getName())) { + handler = new RouteHandlerWrapper(handler); + } + } +} diff --git a/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java8/datadog/trace/instrumentation/vertx_4_0/server/RouteMatchesAdvice.java b/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java8/datadog/trace/instrumentation/vertx_4_0/server/RouteMatchesAdvice.java new file mode 100644 index 00000000000..34053bd8ef0 --- /dev/null +++ b/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java8/datadog/trace/instrumentation/vertx_4_0/server/RouteMatchesAdvice.java @@ -0,0 +1,35 @@ +package datadog.trace.instrumentation.vertx_4_0.server; + +import io.vertx.ext.web.RoutingContext; +import java.util.Map; +import net.bytebuddy.asm.Advice; + +class RouteMatchesAdvice { + @Advice.OnMethodExit(suppress = Throwable.class) + static void after(@Advice.Return int ret, @Advice.Argument(0) final RoutingContext ctx) { + if (ret != 0) { + return; + } + Map params = ctx.pathParams(); + if (params.isEmpty()) { + return; + } + + PathParameterPublishingHelper.publishParams(params); + } + + static class BooleanReturnVariant { + @Advice.OnMethodExit(suppress = Throwable.class) + static void after(@Advice.Return boolean ret, @Advice.Argument(0) final RoutingContext ctx) { + if (!ret) { + return; + } + Map params = ctx.pathParams(); + if (params.isEmpty()) { + return; + } + + PathParameterPublishingHelper.publishParams(params); + } + } +} diff --git a/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java8/datadog/trace/instrumentation/vertx_4_0/server/RoutingContextJsonAdvice.java b/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java8/datadog/trace/instrumentation/vertx_4_0/server/RoutingContextJsonAdvice.java new file mode 100644 index 00000000000..85f4c55e718 --- /dev/null +++ b/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java8/datadog/trace/instrumentation/vertx_4_0/server/RoutingContextJsonAdvice.java @@ -0,0 +1,40 @@ +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 io.vertx.core.json.JsonObject; +import net.bytebuddy.asm.Advice; + +class RoutingContextJsonAdvice { + @Advice.OnMethodExit(suppress = Throwable.class) + static void after(@Advice.Return Object obj_) { + if (obj_ == null) { + return; + } + Object obj = obj_; + if (obj instanceof JsonObject) { + obj = ((JsonObject) obj).getMap(); + } + + AgentSpan agentSpan = activeSpan(); + if (agentSpan == null) { + return; + } + CallbackProvider cbp = AgentTracer.get().instrumentationGateway(); + BiFunction, Object, Flow> callback = + cbp.getCallback(EVENTS.requestBodyProcessed()); + RequestContext requestContext = agentSpan.getRequestContext(); + if (requestContext == null || callback == null) { + return; + } + + callback.apply(requestContext, obj); + } +} diff --git a/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java8/datadog/trace/instrumentation/vertx_4_0/server/VertxDecorator.java b/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java8/datadog/trace/instrumentation/vertx_4_0/server/VertxDecorator.java new file mode 100644 index 00000000000..3458d589d53 --- /dev/null +++ b/dd-java-agent/instrumentation/vertx-web-4.0/src/main/java8/datadog/trace/instrumentation/vertx_4_0/server/VertxDecorator.java @@ -0,0 +1,131 @@ +package datadog.trace.instrumentation.vertx_4_0.server; + +import datadog.trace.bootstrap.instrumentation.api.AgentPropagation; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import datadog.trace.bootstrap.instrumentation.api.URIDataAdapter; +import datadog.trace.bootstrap.instrumentation.api.URIDataAdapterBase; +import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString; +import datadog.trace.bootstrap.instrumentation.decorator.HttpServerDecorator; +import io.vertx.core.http.HttpServerResponse; +import io.vertx.ext.web.RoutingContext; + +public class VertxDecorator + extends HttpServerDecorator { + static final CharSequence INSTRUMENTATION_NAME = UTF8BytesString.create("vertx.route-handler"); + + private static final CharSequence COMPONENT_NAME = UTF8BytesString.create("vertx"); + + static final VertxDecorator DECORATE = new VertxDecorator(); + + @Override + protected String[] instrumentationNames() { + return new String[] {INSTRUMENTATION_NAME.toString()}; + } + + @Override + protected CharSequence component() { + return COMPONENT_NAME; + } + + @Override + protected AgentPropagation.ContextVisitor getter() { + return null; + } + + @Override + protected AgentPropagation.ContextVisitor responseGetter() { + return null; + } + + @Override + public CharSequence spanName() { + return INSTRUMENTATION_NAME; + } + + @Override + protected String method(final RoutingContext routingContext) { + return routingContext.request().method().name(); + } + + @Override + protected URIDataAdapter url(final RoutingContext routingContext) { + return new VertxURIDataAdapter(routingContext); + } + + @Override + public AgentSpan onRequest( + final AgentSpan span, + final RoutingContext connection, + final RoutingContext routingContext, + AgentSpan.Context.Extracted context) { + return span; + } + + @Override + protected String peerHostIP(final RoutingContext routingContext) { + return routingContext.request().connection().remoteAddress().host(); + } + + @Override + protected int peerPort(final RoutingContext routingContext) { + return routingContext.request().connection().remoteAddress().port(); + } + + @Override + protected int status(final HttpServerResponse httpServerResponse) { + return httpServerResponse.getStatusCode(); + } + + protected static final class VertxURIDataAdapter extends URIDataAdapterBase { + private final RoutingContext routingContext; + + public VertxURIDataAdapter(final RoutingContext routingContext) { + this.routingContext = routingContext; + } + + @Override + public String scheme() { + return routingContext.request().scheme(); + } + + @Override + public String host() { + return routingContext.request().host(); + } + + @Override + public int port() { + return routingContext.request().localAddress().port(); + } + + @Override + public String path() { + return routingContext.request().path(); + } + + @Override + public String fragment() { + return null; + } + + @Override + public String query() { + return routingContext.request().query(); + } + + @Override + public boolean supportsRaw() { + return false; + } + + @Override + public String rawPath() { + return null; + } + + @Override + public String rawQuery() { + return null; + } + } +} diff --git a/dd-java-agent/instrumentation/vertx-web-4.0/src/test/groovy/client/VertxHttpClientForkedTest.groovy b/dd-java-agent/instrumentation/vertx-web-4.0/src/test/groovy/client/VertxHttpClientForkedTest.groovy new file mode 100644 index 00000000000..232bff1007a --- /dev/null +++ b/dd-java-agent/instrumentation/vertx-web-4.0/src/test/groovy/client/VertxHttpClientForkedTest.groovy @@ -0,0 +1,81 @@ +package client + +import datadog.trace.agent.test.base.HttpClientTest +import datadog.trace.instrumentation.netty41.client.NettyHttpClientDecorator +import io.vertx.core.Vertx +import io.vertx.core.VertxOptions +import io.vertx.core.http.HttpClientOptions +import io.vertx.core.http.HttpClientResponse +import io.vertx.core.http.HttpMethod +import spock.lang.AutoCleanup +import spock.lang.Shared + +import java.util.concurrent.CompletableFuture +import java.util.concurrent.TimeUnit + +class VertxHttpClientForkedTest extends HttpClientTest { + + @Override + boolean useStrictTraceWrites() { + return false + } + + @AutoCleanup + @Shared + def vertx = Vertx.vertx(new VertxOptions()) + + @Shared + def clientOptions = new HttpClientOptions() + // vertx default is in seconds + .setConnectTimeout(TimeUnit.SECONDS.toSeconds(3) as int) + .setIdleTimeout(TimeUnit.SECONDS.toSeconds(5) as int) + + @AutoCleanup + @Shared + def httpClient = vertx.createHttpClient(clientOptions) + + @Override + int doRequest(String method, URI uri, Map headers, String body, Closure callback) { + CompletableFuture future = new CompletableFuture<>() + + httpClient.request(HttpMethod.valueOf(method), uri.port, uri.host, "$uri", { requestReadyToBeSend -> + def request = requestReadyToBeSend.result() + headers.each { request.putHeader(it.key, it.value) } + request.send(body, { response -> + try { + callback?.call() + future.complete(response.result()) + } catch (Exception e) { + future.completeExceptionally(e) + } + }) + }) + + return future.get(10, TimeUnit.SECONDS).statusCode() + } + + @Override + CharSequence component() { + return NettyHttpClientDecorator.DECORATE.component() + } + + @Override + String expectedOperationName() { + return "netty.client.request" + } + + @Override + boolean testRedirects() { + false + } + + @Override + boolean testConnectionFailure() { + false + } + + boolean testRemoteConnection() { + // FIXME: figure out how to configure timeouts. + false + } +} diff --git a/dd-java-agent/instrumentation/vertx-web-4.0/src/test/groovy/server/VertxHttpServerForkedTest.groovy b/dd-java-agent/instrumentation/vertx-web-4.0/src/test/groovy/server/VertxHttpServerForkedTest.groovy new file mode 100644 index 00000000000..08d235064df --- /dev/null +++ b/dd-java-agent/instrumentation/vertx-web-4.0/src/test/groovy/server/VertxHttpServerForkedTest.groovy @@ -0,0 +1,182 @@ +package server + +import datadog.trace.agent.test.asserts.TraceAssert +import datadog.trace.agent.test.base.HttpServer +import datadog.trace.agent.test.base.HttpServerTest +import datadog.trace.api.DDSpanTypes +import datadog.trace.bootstrap.instrumentation.api.Tags +import datadog.trace.instrumentation.netty41.server.NettyHttpServerDecorator +import datadog.trace.instrumentation.vertx_4_0.server.VertxDecorator +import io.vertx.core.AbstractVerticle +import io.vertx.core.DeploymentOptions +import io.vertx.core.Vertx +import io.vertx.core.impl.VertxInternal +import io.vertx.core.json.JsonObject + +import java.util.concurrent.CompletableFuture + +import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.ERROR +import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.EXCEPTION +import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.LOGIN +import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.NOT_FOUND +import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.PATH_PARAM +import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.SUCCESS +import static server.VertxTestServer.CONFIG_HTTP_SERVER_PORT + +class VertxHttpServerForkedTest extends HttpServerTest { + + private class VertxServer implements HttpServer { + private VertxInternal server + private String routerBasePath + private port + + VertxServer(String routerBasePath) { + this.routerBasePath = routerBasePath + } + + @Override + void start() { + server = Vertx.vertx() + + final CompletableFuture future = new CompletableFuture<>() + server.eventBus().localConsumer("PORT_DATA") + .handler({ message -> + port = message.body() + message.reply(null) + future.complete(null) + }) + + server.deployVerticle(verticle().name, + new DeploymentOptions() + .setConfig(new JsonObject().put(CONFIG_HTTP_SERVER_PORT, 0)) + .setInstances(1)) { res -> + if (!res.succeeded()) { + throw new RuntimeException("Cannot deploy server Verticle", res.cause()) + } + } + future.get() + } + + @Override + void stop() { + server.close() + } + + @Override + URI address() { + return new URI("http://localhost:$port$routerBasePath") + } + } + + @Override + HttpServer server() { + return new VertxServer(routerBasePath()) + } + + protected Class verticle() { + VertxTestServer + } + + String routerBasePath() { + return "/" + } + + @Override + String component() { + return NettyHttpServerDecorator.DECORATE.component() + } + + @Override + String expectedOperationName() { + "netty.request" + } + + @Override + String testPathParam() { + routerBasePath() + "path/:id/param" + } + + @Override + boolean testExceptionBody() { + // Vertx wraps the exception + false + } + + @Override + Map expectedIGPathParams() { + [id: '123'] + } + + @Override + boolean testBodyUrlencoded() { + true + } + + @Override + boolean testBodyJson() { + true + } + + @Override + Class expectedExceptionType() { + return RuntimeException + } + + boolean testExceptionTag() { + true + } + + @Override + boolean hasDecodedResource() { + return false + } + + @Override + int spanCount(ServerEndpoint endpoint) { + if (endpoint == NOT_FOUND) { + return super.spanCount(endpoint) - 1 + } + return super.spanCount(endpoint) + } + + @Override + boolean hasHandlerSpan() { + true + } + + @Override + Serializable expectedServerSpanRoute(ServerEndpoint endpoint) { + switch (endpoint) { + case LOGIN: + case NOT_FOUND: + return null + case PATH_PARAM: + return testPathParam() + default: + return routerBasePath() + endpoint.relativePath() + } + } + + @Override + void handlerSpan(TraceAssert trace, ServerEndpoint endpoint = SUCCESS) { + if (endpoint == NOT_FOUND) { + return + } + trace.span { + serviceName expectedServiceName() + operationName "vertx.route-handler" + spanType DDSpanTypes.HTTP_SERVER + errored endpoint == ERROR || endpoint == EXCEPTION + childOfPrevious() + tags { + "$Tags.COMPONENT" VertxDecorator.DECORATE.component() + "$Tags.SPAN_KIND" Tags.SPAN_KIND_SERVER + "$Tags.HTTP_STATUS" Integer + if (endpoint == EXCEPTION && this.testExceptionTag()) { + errorTags(RuntimeException, EXCEPTION.body) + } + defaultTags() + } + } + } +} diff --git a/dd-java-agent/instrumentation/vertx-web-4.0/src/test/groovy/server/VertxMiddlewareHttpServerForkedTest.groovy b/dd-java-agent/instrumentation/vertx-web-4.0/src/test/groovy/server/VertxMiddlewareHttpServerForkedTest.groovy new file mode 100644 index 00000000000..ca82d1aa6be --- /dev/null +++ b/dd-java-agent/instrumentation/vertx-web-4.0/src/test/groovy/server/VertxMiddlewareHttpServerForkedTest.groovy @@ -0,0 +1,44 @@ +package server + +import datadog.trace.agent.test.asserts.TraceAssert +import datadog.trace.api.DDSpanTypes +import datadog.trace.bootstrap.instrumentation.api.Tags +import datadog.trace.instrumentation.vertx_4_0.server.VertxDecorator +import io.vertx.core.AbstractVerticle + +import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.ERROR +import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.EXCEPTION +import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.SUCCESS + +class VertxMiddlewareHttpServerForkedTest extends VertxHttpServerForkedTest { + @Override + protected Class verticle() { + VertxMiddlewareTestServer + } + + @Override + int spanCount(ServerEndpoint endpoint) { + return 2 + (hasHandlerSpan() ? 1 : 0) + (hasResponseSpan(endpoint) ? 1 : 0) + } + + @Override + void handlerSpan(TraceAssert trace, ServerEndpoint endpoint = SUCCESS) { + trace.span { + serviceName expectedServiceName() + operationName "vertx.route-handler" + spanType DDSpanTypes.HTTP_SERVER + errored endpoint == ERROR || endpoint == EXCEPTION + childOfPrevious() + tags { + "$Tags.COMPONENT" VertxDecorator.DECORATE.component() + "$Tags.SPAN_KIND" Tags.SPAN_KIND_SERVER + "$Tags.HTTP_STATUS" Integer + "before" true + if (endpoint == EXCEPTION && this.testExceptionTag()) { + errorTags(RuntimeException, EXCEPTION.body) + } + defaultTags() + } + } + } +} diff --git a/dd-java-agent/instrumentation/vertx-web-4.0/src/test/groovy/server/VertxSubrouterForkedTest.groovy b/dd-java-agent/instrumentation/vertx-web-4.0/src/test/groovy/server/VertxSubrouterForkedTest.groovy new file mode 100644 index 00000000000..26f5b27d894 --- /dev/null +++ b/dd-java-agent/instrumentation/vertx-web-4.0/src/test/groovy/server/VertxSubrouterForkedTest.groovy @@ -0,0 +1,16 @@ +package server + + +import io.vertx.core.AbstractVerticle + +class VertxSubrouterForkedTest extends VertxHttpServerForkedTest { + @Override + protected Class verticle() { + VertxSubrouterTestServer + } + + @Override + String routerBasePath() { + return "/sub/" + } +} diff --git a/dd-java-agent/instrumentation/vertx-web-4.0/src/test/java/server/VertxMiddlewareTestServer.java b/dd-java-agent/instrumentation/vertx-web-4.0/src/test/java/server/VertxMiddlewareTestServer.java new file mode 100644 index 00000000000..e77f1b6fb65 --- /dev/null +++ b/dd-java-agent/instrumentation/vertx-web-4.0/src/test/java/server/VertxMiddlewareTestServer.java @@ -0,0 +1,17 @@ +package server; + +import datadog.trace.bootstrap.instrumentation.api.AgentTracer; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.RoutingContext; + +public class VertxMiddlewareTestServer extends VertxTestServer { + @Override + protected void customizeBeforeRoutes(Router router) { + router.route().handler(VertxMiddlewareTestServer::firstHandler); + } + + private static void firstHandler(final RoutingContext ctx) { + AgentTracer.activeSpan().setTag("before", true); + ctx.next(); + } +} diff --git a/dd-java-agent/instrumentation/vertx-web-4.0/src/test/java/server/VertxSubrouterTestServer.java b/dd-java-agent/instrumentation/vertx-web-4.0/src/test/java/server/VertxSubrouterTestServer.java new file mode 100644 index 00000000000..27060432e7e --- /dev/null +++ b/dd-java-agent/instrumentation/vertx-web-4.0/src/test/java/server/VertxSubrouterTestServer.java @@ -0,0 +1,12 @@ +package server; + +import io.vertx.ext.web.Router; + +public class VertxSubrouterTestServer extends VertxTestServer { + @Override + protected Router customizeAfterRoutes(Router configured) { + Router router = Router.router(vertx); + router.mountSubRouter("/sub/", configured); + return router; + } +} diff --git a/dd-java-agent/instrumentation/vertx-web-4.0/src/test/java/server/VertxTestServer.java b/dd-java-agent/instrumentation/vertx-web-4.0/src/test/java/server/VertxTestServer.java new file mode 100644 index 00000000000..3c70c21b3e1 --- /dev/null +++ b/dd-java-agent/instrumentation/vertx-web-4.0/src/test/java/server/VertxTestServer.java @@ -0,0 +1,236 @@ +package server; + +import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.BODY_JSON; +import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.BODY_URLENCODED; +import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.CREATED; +import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.ERROR; +import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.EXCEPTION; +import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.FORWARDED; +import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.NOT_FOUND; +import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.PATH_PARAM; +import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.QUERY_ENCODED_BOTH; +import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.QUERY_ENCODED_QUERY; +import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.QUERY_PARAM; +import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.REDIRECT; +import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.SUCCESS; +import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.UNKNOWN; +import static datadog.trace.agent.test.utils.TraceUtils.runnableUnderTraceAsync; +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeScope; +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeSpan; + +import datadog.trace.agent.test.base.HttpServerTest; +import datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint; +import io.vertx.core.AbstractVerticle; +import io.vertx.core.MultiMap; +import io.vertx.core.Promise; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.RoutingContext; +import io.vertx.ext.web.handler.BodyHandler; + +public class VertxTestServer extends AbstractVerticle { + public static final String CONFIG_HTTP_SERVER_PORT = "http.server.port"; + public static final String PORT_DATA_ADDRESS = "PORT_DATA"; + + @Override + public void start(final Promise startPromise) { + final int port = config().getInteger(CONFIG_HTTP_SERVER_PORT); + Router router = Router.router(vertx); + + customizeBeforeRoutes(router); + + router + .route(SUCCESS.getPath()) + .handler( + ctx -> + controller( + ctx, + SUCCESS, + () -> + ctx.response().setStatusCode(SUCCESS.getStatus()).end(SUCCESS.getBody()))); + router + .route(FORWARDED.getPath()) + .handler( + ctx -> + controller( + ctx, + FORWARDED, + () -> + ctx.response() + .setStatusCode(FORWARDED.getStatus()) + .end(ctx.request().getHeader("x-forwarded-for")))); + router + .route(CREATED.getPath()) + .handler( + ctx -> + controller( + ctx, + CREATED, + () -> + ctx.request() + .bodyHandler( + body -> + ctx.response() + .setStatusCode(CREATED.getStatus()) + .end(CREATED.getBody() + ": " + body.toString())))); + router.route(BODY_URLENCODED.getPath()).handler(BodyHandler.create()); + router + .route(BODY_URLENCODED.getPath()) + .handler( + ctx -> + controller( + ctx, + BODY_URLENCODED, + () -> { + String res = "["; + MultiMap entries = ctx.request().formAttributes(); + for (String name : entries.names()) { + if (name.equals("ignore")) { + continue; + } + if (res.length() > 1) { + res += ", "; + } + res += name; + res += ":["; + int i = 0; + for (String s : entries.getAll(name)) { + if (i++ > 0) { + res += ", "; + } + res += s; + } + res += ']'; + } + res += ']'; + ctx.response().setStatusCode(BODY_URLENCODED.getStatus()).end(res); + })); + router.route(BODY_JSON.getPath()).handler(BodyHandler.create()); + router + .route(BODY_JSON.getPath()) + .handler( + ctx -> + controller( + ctx, + BODY_JSON, + () -> { + JsonObject json = ctx.getBodyAsJson(); + ctx.response().setStatusCode(BODY_JSON.getStatus()).end(json.toString()); + })); + router + .route(QUERY_ENCODED_BOTH.getRawPath()) + .handler( + ctx -> + controller( + ctx, + QUERY_ENCODED_BOTH, + () -> + ctx.response() + .setStatusCode(QUERY_ENCODED_BOTH.getStatus()) + .end(QUERY_ENCODED_BOTH.bodyForQuery(ctx.request().query())))); + router + .route(QUERY_ENCODED_QUERY.getPath()) + .handler( + ctx -> + controller( + ctx, + QUERY_ENCODED_QUERY, + () -> + ctx.response() + .setStatusCode(QUERY_ENCODED_QUERY.getStatus()) + .end(QUERY_ENCODED_QUERY.bodyForQuery(ctx.request().query())))); + router + .route(QUERY_PARAM.getPath()) + .handler( + ctx -> + controller( + ctx, + QUERY_PARAM, + () -> + ctx.response() + .setStatusCode(QUERY_PARAM.getStatus()) + .end(ctx.request().query()))); + router + .route("/path/:id/param") + .handler( + ctx -> + controller( + ctx, + PATH_PARAM, + () -> + ctx.response() + .setStatusCode(PATH_PARAM.getStatus()) + .end(ctx.request().getParam("id")))); + router + .route(REDIRECT.getPath()) + .handler( + ctx -> + controller( + ctx, + REDIRECT, + () -> + ctx.response() + .setStatusCode(REDIRECT.getStatus()) + .putHeader("location", REDIRECT.getBody()) + .end())); + router + .route(ERROR.getPath()) + .handler( + ctx -> + controller( + ctx, + ERROR, + () -> ctx.response().setStatusCode(ERROR.getStatus()).end(ERROR.getBody()))); + router + .route(EXCEPTION.getPath()) + .handler(ctx -> controller(ctx, EXCEPTION, VertxTestServer::exception)); + + router = customizeAfterRoutes(router); + + vertx + .createHttpServer() + .requestHandler(router) + .listen( + port, + event -> { + // send this though event bus and succed deploy after successfull response + int actualPort = event.result().actualPort(); + vertx + .eventBus() + .request( + PORT_DATA_ADDRESS, + actualPort, + ar -> { + if (ar.succeeded()) { + startPromise.complete(); + } else { + startPromise.fail(ar.cause()); + } + }); + }); + } + + protected void customizeBeforeRoutes(Router router) {} + + protected Router customizeAfterRoutes(final Router router) { + return router; + } + + private static void exception() { + throw new RuntimeException(EXCEPTION.getBody()); + } + + private static void controller( + RoutingContext ctx, final ServerEndpoint endpoint, final Runnable runnable) { + assert activeSpan() != null : "Controller should have a parent span."; + assert activeScope().isAsyncPropagating() : "Scope should be propagating async."; + ctx.response() + .putHeader( + HttpServerTest.getIG_RESPONSE_HEADER(), HttpServerTest.getIG_RESPONSE_HEADER_VALUE()); + if (endpoint == NOT_FOUND || endpoint == UNKNOWN) { + runnable.run(); + return; + } + runnableUnderTraceAsync("controller", runnable); + } +} diff --git a/dd-java-agent/instrumentation/vertx-web-4.0/vertx-web-4.0.gradle b/dd-java-agent/instrumentation/vertx-web-4.0/vertx-web-4.0.gradle new file mode 100644 index 00000000000..2e31de2c789 --- /dev/null +++ b/dd-java-agent/instrumentation/vertx-web-4.0/vertx-web-4.0.gradle @@ -0,0 +1,47 @@ +// Set properties before any plugins get loaded +ext { + minJavaVersionForTests = JavaVersion.VERSION_1_8 + // TODO Java 17: This version of vertx-web doesn't support Java 17 + maxJavaVersionForTests = JavaVersion.VERSION_15 +} + +apply from: "$rootDir/gradle/java.gradle" + +apply plugin: 'org.unbroken-dome.test-sets' + +muzzle { + pass { + group = 'io.vertx' + module = "vertx-web" + versions = "[4.0.0,5)" + assertInverse = true + } +} + +testSets { + latestDepTest { + dirName = 'test' + } +} + +configurations { + testArtifacts +} + +// Create test artifacts so vertx-rx can reuse the server test instrumentation and base class +artifacts { + testArtifacts testJar +} + +dependencies { + main_java8CompileOnly group: 'io.vertx', name: 'vertx-web', version: '4.2.7' + + testImplementation project(':dd-java-agent:instrumentation:netty-4.1') + testImplementation project(':dd-java-agent:instrumentation:trace-annotation') + + testImplementation group: 'io.vertx', name: 'vertx-web', version: '4.0.0' + testImplementation group: 'io.vertx', name: 'vertx-web-client', version: '4.0.0' + + latestDepTestImplementation group: 'io.vertx', name: 'vertx-web', version: '4.+' + latestDepTestImplementation group: 'io.vertx', name: 'vertx-web-client', version: '4.+' +} diff --git a/settings.gradle b/settings.gradle index 1d35d9670da..6e154a366e1 100644 --- a/settings.gradle +++ b/settings.gradle @@ -287,6 +287,7 @@ include ':dd-java-agent:instrumentation:vertx-redis-client-3.9' include ':dd-java-agent:instrumentation:vertx-redis-client-3.9:stubs' include ':dd-java-agent:instrumentation:vertx-rx-3.5' include ':dd-java-agent:instrumentation:vertx-web-3.4' +include ':dd-java-agent:instrumentation:vertx-web-4.0' include ':dd-java-agent:instrumentation:redisson-2.0.0' // benchmark