From f550e8770ac7a6c503e562112e083396d510713c Mon Sep 17 00:00:00 2001 From: Andrea Marziali Date: Wed, 13 May 2026 14:19:15 +0200 Subject: [PATCH] Avoid several per request string allocations in spring-webmvc instrumenters --- .../HandlerAdapterInstrumentation.java | 27 ++++++----------- ...InvocableHandlerMethodInstrumentation.java | 16 +++++----- .../SpringWebHttpServerDecorator.java | 30 +++++++++++++++++-- .../springweb6/ControllerAdvice.java | 28 +++++++---------- .../SpringWebHttpServerDecorator.java | 22 ++++++++++++-- .../WrapContinuableResultAdvice.java | 13 ++++---- 6 files changed, 82 insertions(+), 54 deletions(-) diff --git a/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-3.1/src/main/java/datadog/trace/instrumentation/springweb/HandlerAdapterInstrumentation.java b/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-3.1/src/main/java/datadog/trace/instrumentation/springweb/HandlerAdapterInstrumentation.java index ea8282e5681..0cf96151794 100644 --- a/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-3.1/src/main/java/datadog/trace/instrumentation/springweb/HandlerAdapterInstrumentation.java +++ b/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-3.1/src/main/java/datadog/trace/instrumentation/springweb/HandlerAdapterInstrumentation.java @@ -9,9 +9,8 @@ import static datadog.trace.bootstrap.instrumentation.api.Java8BytecodeBridge.getRootContext; import static datadog.trace.bootstrap.instrumentation.api.Java8BytecodeBridge.spanFromContext; import static datadog.trace.bootstrap.instrumentation.decorator.HttpServerDecorator.DD_CONTEXT_ATTRIBUTE; -import static datadog.trace.instrumentation.springweb.SpringWebHttpServerDecorator.DD_HANDLER_SPAN_CONTINUE_SUFFIX; -import static datadog.trace.instrumentation.springweb.SpringWebHttpServerDecorator.DD_HANDLER_SPAN_PREFIX_KEY; import static datadog.trace.instrumentation.springweb.SpringWebHttpServerDecorator.DECORATE; +import static datadog.trace.instrumentation.springweb.SpringWebHttpServerDecorator.handlerSpanKeysFor; import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.takesArgument; @@ -22,12 +21,12 @@ import datadog.context.ContextScope; import datadog.trace.agent.tooling.Instrumenter; import datadog.trace.agent.tooling.InstrumenterModule; +import datadog.trace.api.Pair; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import javax.servlet.http.HttpServletRequest; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; -import org.springframework.web.method.HandlerMethod; @AutoService(InstrumenterModule.class) public final class HandlerAdapterInstrumentation extends InstrumenterModule.Tracing @@ -71,8 +70,7 @@ public static class ControllerAdvice { public static ContextScope nameResourceAndStartSpan( @Advice.Argument(0) final HttpServletRequest request, @Advice.Argument(2) final Object handler, - @Advice.Local("handlerSpanKey") String handlerSpanKey) { - handlerSpanKey = ""; + @Advice.Local("handlerSpanKeys") Pair handlerSpanKeys) { // Name the parent span based on the matching pattern Object contextObj = request.getAttribute(DD_CONTEXT_ATTRIBUTE); @@ -89,17 +87,10 @@ public static ContextScope nameResourceAndStartSpan( } // Now create a span for handler/controller execution. - - final String handlerKey; - if (handler instanceof HandlerMethod) { - handlerKey = ((HandlerMethod) handler).getBean().getClass().getName(); - } else { - handlerKey = handler.getClass().getName(); - } - handlerSpanKey = DD_HANDLER_SPAN_PREFIX_KEY + handlerKey; + handlerSpanKeys = handlerSpanKeysFor(handler); // If the context already exists, return it - final Object existingContext = request.getAttribute(handlerSpanKey); + final Object existingContext = request.getAttribute(handlerSpanKeys.getLeft()); if (existingContext instanceof Context) { return ((Context) existingContext).attach(); } @@ -109,7 +100,7 @@ public static ContextScope nameResourceAndStartSpan( DECORATE.afterStart(span); DECORATE.onHandle(span, handler); - request.setAttribute(handlerSpanKey, span); + request.setAttribute(handlerSpanKeys.getLeft(), span); return getCurrentContext().with(span).attach(); } @@ -118,13 +109,13 @@ public static void stopSpan( @Advice.Argument(0) final HttpServletRequest request, @Advice.Enter final ContextScope scope, @Advice.Thrown final Throwable throwable, - @Advice.Local("handlerSpanKey") String handlerSpanKey) { + @Advice.Local("handlerSpanKeys") Pair handlerSpanKeys) { if (scope == null) { return; } boolean finish = - !Boolean.TRUE.equals( - request.getAttribute(handlerSpanKey + DD_HANDLER_SPAN_CONTINUE_SUFFIX)); + handlerSpanKeys != null + && !Boolean.TRUE.equals(request.getAttribute(handlerSpanKeys.getRight())); final AgentSpan span = spanFromContext(scope.context()); scope.close(); if (throwable != null) { diff --git a/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-3.1/src/main/java/datadog/trace/instrumentation/springweb/InvocableHandlerMethodInstrumentation.java b/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-3.1/src/main/java/datadog/trace/instrumentation/springweb/InvocableHandlerMethodInstrumentation.java index a4b26041f02..9bd0f253052 100644 --- a/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-3.1/src/main/java/datadog/trace/instrumentation/springweb/InvocableHandlerMethodInstrumentation.java +++ b/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-3.1/src/main/java/datadog/trace/instrumentation/springweb/InvocableHandlerMethodInstrumentation.java @@ -1,12 +1,12 @@ package datadog.trace.instrumentation.springweb; import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; -import static datadog.trace.instrumentation.springweb.SpringWebHttpServerDecorator.DD_HANDLER_SPAN_CONTINUE_SUFFIX; -import static datadog.trace.instrumentation.springweb.SpringWebHttpServerDecorator.DD_HANDLER_SPAN_PREFIX_KEY; +import static datadog.trace.instrumentation.springweb.SpringWebHttpServerDecorator.handlerSpanKeysFor; import com.google.auto.service.AutoService; import datadog.trace.agent.tooling.Instrumenter; import datadog.trace.agent.tooling.InstrumenterModule; +import datadog.trace.api.Pair; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.java.concurrent.AsyncResultExtensions; import java.util.concurrent.CompletionStage; @@ -53,21 +53,21 @@ public static void after( return; } ServletWebRequest servletWebRequest = (ServletWebRequest) nativeWebRequest; - final String handlerSpanKey = - DD_HANDLER_SPAN_PREFIX_KEY + self.getBean().getClass().getName(); - + final Pair handlerSpanKeys = handlerSpanKeysFor(self.getBean()); if (Boolean.TRUE.equals( servletWebRequest.getAttribute( - handlerSpanKey + DD_HANDLER_SPAN_CONTINUE_SUFFIX, ServletWebRequest.SCOPE_REQUEST))) { + handlerSpanKeys.getRight(), ServletWebRequest.SCOPE_REQUEST))) { return; } - Object span = servletWebRequest.getAttribute(handlerSpanKey, ServletWebRequest.SCOPE_REQUEST); + Object span = + servletWebRequest.getAttribute( + handlerSpanKeys.getLeft(), ServletWebRequest.SCOPE_REQUEST); if (!(span instanceof AgentSpan)) { return; } servletWebRequest.setAttribute( - handlerSpanKey + DD_HANDLER_SPAN_CONTINUE_SUFFIX, true, ServletWebRequest.SCOPE_REQUEST); + handlerSpanKeys.getRight(), true, ServletWebRequest.SCOPE_REQUEST); result = ((CompletionStage) result) .whenComplete(AsyncResultExtensions.finishSpan((AgentSpan) span)); diff --git a/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-3.1/src/main/java/datadog/trace/instrumentation/springweb/SpringWebHttpServerDecorator.java b/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-3.1/src/main/java/datadog/trace/instrumentation/springweb/SpringWebHttpServerDecorator.java index 52ce8855048..de95d04edec 100644 --- a/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-3.1/src/main/java/datadog/trace/instrumentation/springweb/SpringWebHttpServerDecorator.java +++ b/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-3.1/src/main/java/datadog/trace/instrumentation/springweb/SpringWebHttpServerDecorator.java @@ -3,6 +3,8 @@ import static datadog.trace.bootstrap.instrumentation.decorator.http.HttpResourceDecorator.HTTP_RESOURCE_DECORATOR; import datadog.context.Context; +import datadog.trace.api.GenericClassValue; +import datadog.trace.api.Pair; import datadog.trace.bootstrap.instrumentation.api.AgentPropagation; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.URIDataAdapter; @@ -21,6 +23,15 @@ public class SpringWebHttpServerDecorator extends HttpServerDecorator { + private static final String DD_HANDLER_SPAN_PREFIX_KEY = "dd.handler.span."; + private static final String DD_HANDLER_SPAN_CONTINUE_SUFFIX = ".continue"; + private static final ClassValue> HANDLER_SPAN_KEY_CACHE = + GenericClassValue.of( + type -> { + final String prefix = DD_HANDLER_SPAN_PREFIX_KEY + type.getName(); + return Pair.of(prefix, prefix + DD_HANDLER_SPAN_CONTINUE_SUFFIX); + }); + private static final CharSequence SPRING_HANDLER = UTF8BytesString.create("spring.handler"); public static final CharSequence RESPONSE_RENDER = UTF8BytesString.create("response.render"); @@ -30,8 +41,23 @@ public class SpringWebHttpServerDecorator new SpringWebHttpServerDecorator(UTF8BytesString.create("spring-web-controller")); public static final SpringWebHttpServerDecorator DECORATE_RENDER = new SpringWebHttpServerDecorator(UTF8BytesString.create("spring-webmvc")); - public static final String DD_HANDLER_SPAN_PREFIX_KEY = "dd.handler.span."; - public static final String DD_HANDLER_SPAN_CONTINUE_SUFFIX = ".continue"; + + /** + * Returns cached values for handler span key and its continue suffix. Avoid allocating strings. + * + * @param handler the spring handler. + * @return a pair whose left is DD_HANDLER_SPAN_PREFIX_KEY + typeName and the right has also + * DD_HANDLER_SPAN_CONTINUE_SUFFIX suffix. + */ + public static Pair handlerSpanKeysFor(Object handler) { + final Class handlerClass; + if (handler instanceof HandlerMethod) { + handlerClass = ((HandlerMethod) handler).getBean().getClass(); + } else { + handlerClass = handler.getClass(); + } + return HANDLER_SPAN_KEY_CACHE.get(handlerClass); + } public SpringWebHttpServerDecorator(CharSequence component) { this.component = component; diff --git a/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/src/main/java17/datadog/trace/instrumentation/springweb6/ControllerAdvice.java b/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/src/main/java17/datadog/trace/instrumentation/springweb6/ControllerAdvice.java index 5a675262cad..39184085a34 100644 --- a/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/src/main/java17/datadog/trace/instrumentation/springweb6/ControllerAdvice.java +++ b/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/src/main/java17/datadog/trace/instrumentation/springweb6/ControllerAdvice.java @@ -6,16 +6,15 @@ import static datadog.trace.bootstrap.instrumentation.api.Java8BytecodeBridge.getCurrentContext; import static datadog.trace.bootstrap.instrumentation.api.Java8BytecodeBridge.spanFromContext; import static datadog.trace.bootstrap.instrumentation.decorator.HttpServerDecorator.DD_CONTEXT_ATTRIBUTE; -import static datadog.trace.instrumentation.springweb6.SpringWebHttpServerDecorator.DD_HANDLER_SPAN_CONTINUE_SUFFIX; -import static datadog.trace.instrumentation.springweb6.SpringWebHttpServerDecorator.DD_HANDLER_SPAN_PREFIX_KEY; import static datadog.trace.instrumentation.springweb6.SpringWebHttpServerDecorator.DECORATE; +import static datadog.trace.instrumentation.springweb6.SpringWebHttpServerDecorator.handlerSpanKeysFor; import datadog.context.Context; import datadog.context.ContextScope; +import datadog.trace.api.Pair; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import jakarta.servlet.http.HttpServletRequest; import net.bytebuddy.asm.Advice; -import org.springframework.web.method.HandlerMethod; public class ControllerAdvice { @@ -23,8 +22,8 @@ public class ControllerAdvice { public static ContextScope nameResourceAndStartSpan( @Advice.Argument(0) final HttpServletRequest request, @Advice.Argument(2) final Object handler, - @Advice.Local("handlerSpanKey") String handlerSpanKey) { - handlerSpanKey = ""; + @Advice.Local("handlerSpanKeys") Pair handlerSpanKeys) { + // Name the parent span based on the matching pattern Object contextObj = request.getAttribute(DD_CONTEXT_ATTRIBUTE); if (contextObj instanceof Context) { @@ -40,17 +39,10 @@ public static ContextScope nameResourceAndStartSpan( } // Now create a span for handler/controller execution. - - final String handlerKey; - if (handler instanceof HandlerMethod) { - handlerKey = ((HandlerMethod) handler).getBean().getClass().getName(); - } else { - handlerKey = handler.getClass().getName(); - } - handlerSpanKey = DD_HANDLER_SPAN_PREFIX_KEY + handlerKey; + handlerSpanKeys = handlerSpanKeysFor(handler); // If the context already exists, return it - final Object existingContext = request.getAttribute(handlerSpanKey); + final Object existingContext = request.getAttribute(handlerSpanKeys.getLeft()); if (existingContext instanceof Context) { return ((Context) existingContext).attach(); } @@ -60,7 +52,7 @@ public static ContextScope nameResourceAndStartSpan( DECORATE.afterStart(span); DECORATE.onHandle(span, handler); - request.setAttribute(handlerSpanKey, span); + request.setAttribute(handlerSpanKeys.getLeft(), span); return getCurrentContext().with(span).attach(); } @@ -69,13 +61,13 @@ public static void stopSpan( @Advice.Enter final ContextScope scope, @Advice.Argument(0) final HttpServletRequest request, @Advice.Thrown final Throwable throwable, - @Advice.Local("handlerSpanKey") String handlerSpanKey) { + @Advice.Local("handlerSpanKeys") Pair handlerSpanKeys) { if (scope == null) { return; } boolean finish = - !Boolean.TRUE.equals( - request.getAttribute(handlerSpanKey + DD_HANDLER_SPAN_CONTINUE_SUFFIX)); + handlerSpanKeys != null + && !Boolean.TRUE.equals(request.getAttribute(handlerSpanKeys.getRight())); final AgentSpan span = spanFromContext(scope.context()); scope.close(); if (throwable != null) { diff --git a/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/src/main/java17/datadog/trace/instrumentation/springweb6/SpringWebHttpServerDecorator.java b/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/src/main/java17/datadog/trace/instrumentation/springweb6/SpringWebHttpServerDecorator.java index 79b89c1aad5..0c4a9dece6b 100644 --- a/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/src/main/java17/datadog/trace/instrumentation/springweb6/SpringWebHttpServerDecorator.java +++ b/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/src/main/java17/datadog/trace/instrumentation/springweb6/SpringWebHttpServerDecorator.java @@ -3,6 +3,8 @@ import static datadog.trace.bootstrap.instrumentation.decorator.http.HttpResourceDecorator.HTTP_RESOURCE_DECORATOR; import datadog.context.Context; +import datadog.trace.api.GenericClassValue; +import datadog.trace.api.Pair; import datadog.trace.bootstrap.instrumentation.api.AgentPropagation; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.URIDataAdapter; @@ -24,6 +26,15 @@ public class SpringWebHttpServerDecorator private static final String DD_FILTERED_SPRING_ROUTE_ALREADY_APPLIED = "datadog.filter.spring.route.applied"; + private static final String DD_HANDLER_SPAN_PREFIX_KEY = "dd.handler.span."; + private static final String DD_HANDLER_SPAN_CONTINUE_SUFFIX = ".continue"; + private static final ClassValue> HANDLER_SPAN_KEY_CACHE = + GenericClassValue.of( + type -> { + final String prefix = DD_HANDLER_SPAN_PREFIX_KEY + type.getName(); + return Pair.of(prefix, prefix + DD_HANDLER_SPAN_CONTINUE_SUFFIX); + }); + private static final CharSequence SPRING_HANDLER = UTF8BytesString.create("spring.handler"); public static final CharSequence RESPONSE_RENDER = UTF8BytesString.create("response.render"); @@ -34,8 +45,15 @@ public class SpringWebHttpServerDecorator public static final SpringWebHttpServerDecorator DECORATE_RENDER = new SpringWebHttpServerDecorator(UTF8BytesString.create("spring-webmvc")); - public static final String DD_HANDLER_SPAN_PREFIX_KEY = "dd.handler.span."; - public static final String DD_HANDLER_SPAN_CONTINUE_SUFFIX = ".continue"; + public static Pair handlerSpanKeysFor(Object handler) { + final Class handlerClass; + if (handler instanceof HandlerMethod) { + handlerClass = ((HandlerMethod) handler).getBean().getClass(); + } else { + handlerClass = handler.getClass(); + } + return HANDLER_SPAN_KEY_CACHE.get(handlerClass); + } public SpringWebHttpServerDecorator(CharSequence component) { this.component = component; diff --git a/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/src/main/java17/datadog/trace/instrumentation/springweb6/WrapContinuableResultAdvice.java b/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/src/main/java17/datadog/trace/instrumentation/springweb6/WrapContinuableResultAdvice.java index 4939751da1b..3de08165569 100644 --- a/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/src/main/java17/datadog/trace/instrumentation/springweb6/WrapContinuableResultAdvice.java +++ b/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/src/main/java17/datadog/trace/instrumentation/springweb6/WrapContinuableResultAdvice.java @@ -1,8 +1,8 @@ package datadog.trace.instrumentation.springweb6; -import static datadog.trace.instrumentation.springweb6.SpringWebHttpServerDecorator.DD_HANDLER_SPAN_CONTINUE_SUFFIX; -import static datadog.trace.instrumentation.springweb6.SpringWebHttpServerDecorator.DD_HANDLER_SPAN_PREFIX_KEY; +import static datadog.trace.instrumentation.springweb6.SpringWebHttpServerDecorator.handlerSpanKeysFor; +import datadog.trace.api.Pair; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.java.concurrent.AsyncResultExtensions; import java.util.concurrent.CompletionStage; @@ -24,19 +24,20 @@ public static void after( } ServletWebRequest servletWebRequest = (ServletWebRequest) nativeWebRequest; - final String handlerSpanKey = DD_HANDLER_SPAN_PREFIX_KEY + self.getBean().getClass().getName(); + final Pair handlerSpanKeys = handlerSpanKeysFor(self.getBean()); if (Boolean.TRUE.equals( servletWebRequest.getAttribute( - handlerSpanKey + DD_HANDLER_SPAN_CONTINUE_SUFFIX, ServletWebRequest.SCOPE_REQUEST))) { + handlerSpanKeys.getRight(), ServletWebRequest.SCOPE_REQUEST))) { return; } - Object span = servletWebRequest.getAttribute(handlerSpanKey, ServletWebRequest.SCOPE_REQUEST); + Object span = + servletWebRequest.getAttribute(handlerSpanKeys.getLeft(), ServletWebRequest.SCOPE_REQUEST); if (!(span instanceof AgentSpan)) { return; } servletWebRequest.setAttribute( - handlerSpanKey + DD_HANDLER_SPAN_CONTINUE_SUFFIX, true, ServletWebRequest.SCOPE_REQUEST); + handlerSpanKeys.getRight(), true, ServletWebRequest.SCOPE_REQUEST); result = ((CompletionStage) result) .whenComplete(AsyncResultExtensions.finishSpan((AgentSpan) span));