diff --git a/dd-java-agent/instrumentation/spring-web/src/main/java/datadog/trace/instrumentation/springweb/SpringWebErrorInstrumentation.java b/dd-java-agent/instrumentation/spring-web/src/main/java/datadog/trace/instrumentation/springweb/DispatcherServletInstrumentation.java similarity index 54% rename from dd-java-agent/instrumentation/spring-web/src/main/java/datadog/trace/instrumentation/springweb/SpringWebErrorInstrumentation.java rename to dd-java-agent/instrumentation/spring-web/src/main/java/datadog/trace/instrumentation/springweb/DispatcherServletInstrumentation.java index c4b096156f..47a36459af 100644 --- a/dd-java-agent/instrumentation/spring-web/src/main/java/datadog/trace/instrumentation/springweb/SpringWebErrorInstrumentation.java +++ b/dd-java-agent/instrumentation/spring-web/src/main/java/datadog/trace/instrumentation/springweb/DispatcherServletInstrumentation.java @@ -1,18 +1,17 @@ package datadog.trace.instrumentation.springweb; import static io.opentracing.log.Fields.ERROR_OBJECT; -import static net.bytebuddy.matcher.ElementMatchers.isInterface; import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isProtected; import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; import static net.bytebuddy.matcher.ElementMatchers.named; -import static net.bytebuddy.matcher.ElementMatchers.not; import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import com.google.auto.service.AutoService; import datadog.trace.agent.tooling.Instrumenter; import io.opentracing.Scope; import io.opentracing.Span; +import io.opentracing.Tracer; import io.opentracing.tag.Tags; import io.opentracing.util.GlobalTracer; import java.util.Collections; @@ -21,32 +20,70 @@ import java.util.Map; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; +import org.springframework.web.servlet.ModelAndView; @AutoService(Instrumenter.class) -public final class SpringWebErrorInstrumentation extends Instrumenter.Default { +public final class DispatcherServletInstrumentation extends Instrumenter.Default { - public SpringWebErrorInstrumentation() { + public DispatcherServletInstrumentation() { super("spring-web"); } @Override public ElementMatcher typeMatcher() { - return not(isInterface()).and(named("org.springframework.web.servlet.DispatcherServlet")); + return named("org.springframework.web.servlet.DispatcherServlet"); } @Override public Map transformers() { final Map transformers = new HashMap<>(); + transformers.put( + isMethod() + .and(isProtected()) + .and(named("render")) + .and(takesArgument(0, named("org.springframework.web.servlet.ModelAndView"))), + DispatcherAdvice.class.getName()); transformers.put( isMethod() .and(isProtected()) .and(nameStartsWith("processHandlerException")) .and(takesArgument(3, Exception.class)), - SpringWebErrorHandlerAdvice.class.getName()); + ErrorHandlerAdvice.class.getName()); return transformers; } - public static class SpringWebErrorHandlerAdvice { + public static class DispatcherAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static Scope startSpan(@Advice.Argument(0) final ModelAndView mv) { + + final Tracer.SpanBuilder builder = + GlobalTracer.get() + .buildSpan("response.render") + .withTag(Tags.COMPONENT.getKey(), "spring-webmvc"); + if (mv.getViewName() != null) { + builder.withTag("view.name", mv.getViewName()); + } + if (mv.getView() != null) { + builder.withTag("view.type", mv.getView().getClass().getName()); + } + return builder.startActive(true); + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void stopSpan( + @Advice.Enter final Scope scope, @Advice.Thrown final Throwable throwable) { + + if (throwable != null) { + final Span span = scope.span(); + Tags.ERROR.set(span, true); + span.log(Collections.singletonMap(ERROR_OBJECT, throwable)); + } + scope.close(); + } + } + + public static class ErrorHandlerAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) public static void nameResource(@Advice.Argument(3) final Exception exception) { final Scope scope = GlobalTracer.get().scopeManager().active(); diff --git a/dd-java-agent/instrumentation/spring-web/src/main/java/datadog/trace/instrumentation/springweb/SpringWebInstrumentation.java b/dd-java-agent/instrumentation/spring-web/src/main/java/datadog/trace/instrumentation/springweb/HandlerAdapterInstrumentation.java similarity index 95% rename from dd-java-agent/instrumentation/spring-web/src/main/java/datadog/trace/instrumentation/springweb/SpringWebInstrumentation.java rename to dd-java-agent/instrumentation/spring-web/src/main/java/datadog/trace/instrumentation/springweb/HandlerAdapterInstrumentation.java index f14edf8148..2ba50b9b9d 100644 --- a/dd-java-agent/instrumentation/spring-web/src/main/java/datadog/trace/instrumentation/springweb/SpringWebInstrumentation.java +++ b/dd-java-agent/instrumentation/spring-web/src/main/java/datadog/trace/instrumentation/springweb/HandlerAdapterInstrumentation.java @@ -22,7 +22,6 @@ import io.opentracing.tag.Tags; import io.opentracing.util.GlobalTracer; import java.lang.reflect.Method; import java.util.Collections; -import java.util.HashMap; import java.util.Map; import javax.servlet.Servlet; import javax.servlet.http.HttpServletRequest; @@ -35,9 +34,9 @@ import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.servlet.mvc.Controller; @AutoService(Instrumenter.class) -public final class SpringWebInstrumentation extends Instrumenter.Default { +public final class HandlerAdapterInstrumentation extends Instrumenter.Default { - public SpringWebInstrumentation() { + public HandlerAdapterInstrumentation() { super("spring-web"); } @@ -55,15 +54,13 @@ public final class SpringWebInstrumentation extends Instrumenter.Default { @Override public Map transformers() { - final Map transformers = new HashMap<>(); - transformers.put( + return Collections.singletonMap( isMethod() .and(isPublic()) .and(nameStartsWith("handle")) .and(takesArgument(0, named("javax.servlet.http.HttpServletRequest"))) .and(takesArguments(3)), ControllerAdvice.class.getName()); - return transformers; } public static class ControllerAdvice {