Add instrumentation showing the rendering time
RENAME
dd-java-agent/instrumentation/spring-web/src/main/java/datadog/trace/instrumentation/springweb/SpringWebErrorInstrumentation.java
dd-java-agent/instrumentation/spring-web/src/main/java/datadog/trace/instrumentation/springweb/DispatcherServletInstrumentation.java
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<TypeDescription> typeMatcher() {
- return not(isInterface()).and(named("org.springframework.web.servlet.DispatcherServlet"));
+ return named("org.springframework.web.servlet.DispatcherServlet");
}
@Override
public Map<ElementMatcher, String> transformers() {
final Map<ElementMatcher, String> 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();

RENAME
dd-java-agent/instrumentation/spring-web/src/main/java/datadog/trace/instrumentation/springweb/SpringWebInstrumentation.java
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<ElementMatcher, String> transformers() {
- final Map<ElementMatcher, String> transformers = new HashMap<>();
- transformers.put(
+ return Collections.<ElementMatcher, String>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 {

Add instrumentation showing the rendering time