Skip to content

Commit 72064d6

Browse files
dougqhclaude
andcommitted
Cache handler span keys in spring-webmvc to avoid per-request string concatenation
The handler span key (used as a request attribute name) was computed via string concatenation on every request entry and exit. Replace with a ClassValue-based cache in SpringWebHttpServerDecorator so each handler class computes its key only once. Uses GenericClassValue.of() following the established codebase pattern to avoid anonymous inner class issues with ByteBuddy helper class injection. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 3fb3733 commit 72064d6

6 files changed

Lines changed: 62 additions & 33 deletions

File tree

dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-3.1/src/main/java/datadog/trace/instrumentation/springweb/HandlerAdapterInstrumentation.java

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
import static datadog.trace.bootstrap.instrumentation.api.Java8BytecodeBridge.getRootContext;
1010
import static datadog.trace.bootstrap.instrumentation.api.Java8BytecodeBridge.spanFromContext;
1111
import static datadog.trace.bootstrap.instrumentation.decorator.HttpServerDecorator.DD_CONTEXT_ATTRIBUTE;
12-
import static datadog.trace.instrumentation.springweb.SpringWebHttpServerDecorator.DD_HANDLER_SPAN_CONTINUE_SUFFIX;
13-
import static datadog.trace.instrumentation.springweb.SpringWebHttpServerDecorator.DD_HANDLER_SPAN_PREFIX_KEY;
1412
import static datadog.trace.instrumentation.springweb.SpringWebHttpServerDecorator.DECORATE;
13+
import static datadog.trace.instrumentation.springweb.SpringWebHttpServerDecorator.handlerSpanContinueKey;
14+
import static datadog.trace.instrumentation.springweb.SpringWebHttpServerDecorator.handlerSpanKey;
1515
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
1616
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
1717
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
@@ -71,8 +71,8 @@ public static class ControllerAdvice {
7171
public static ContextScope nameResourceAndStartSpan(
7272
@Advice.Argument(0) final HttpServletRequest request,
7373
@Advice.Argument(2) final Object handler,
74-
@Advice.Local("handlerSpanKey") String handlerSpanKey) {
75-
handlerSpanKey = "";
74+
@Advice.Local("handlerClass") Class handlerClass) {
75+
handlerClass = null;
7676

7777
// Name the parent span based on the matching pattern
7878
Object contextObj = request.getAttribute(DD_CONTEXT_ATTRIBUTE);
@@ -90,13 +90,12 @@ public static ContextScope nameResourceAndStartSpan(
9090

9191
// Now create a span for handler/controller execution.
9292

93-
final String handlerKey;
9493
if (handler instanceof HandlerMethod) {
95-
handlerKey = ((HandlerMethod) handler).getBean().getClass().getName();
94+
handlerClass = ((HandlerMethod) handler).getBean().getClass();
9695
} else {
97-
handlerKey = handler.getClass().getName();
96+
handlerClass = handler.getClass();
9897
}
99-
handlerSpanKey = DD_HANDLER_SPAN_PREFIX_KEY + handlerKey;
98+
final String handlerSpanKey = handlerSpanKey(handlerClass);
10099

101100
// If the context already exists, return it
102101
final Object existingContext = request.getAttribute(handlerSpanKey);
@@ -117,13 +116,12 @@ public static void stopSpan(
117116
@Advice.Argument(0) final HttpServletRequest request,
118117
@Advice.Enter final ContextScope scope,
119118
@Advice.Thrown final Throwable throwable,
120-
@Advice.Local("handlerSpanKey") String handlerSpanKey) {
119+
@Advice.Local("handlerClass") Class handlerClass) {
121120
if (scope == null) {
122121
return;
123122
}
124123
boolean finish =
125-
!Boolean.TRUE.equals(
126-
request.getAttribute(handlerSpanKey + DD_HANDLER_SPAN_CONTINUE_SUFFIX));
124+
!Boolean.TRUE.equals(request.getAttribute(handlerSpanContinueKey(handlerClass)));
127125
final AgentSpan span = spanFromContext(scope.context());
128126
scope.close();
129127
if (throwable != null) {

dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-3.1/src/main/java/datadog/trace/instrumentation/springweb/InvocableHandlerMethodInstrumentation.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package datadog.trace.instrumentation.springweb;
22

33
import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
4-
import static datadog.trace.instrumentation.springweb.SpringWebHttpServerDecorator.DD_HANDLER_SPAN_CONTINUE_SUFFIX;
5-
import static datadog.trace.instrumentation.springweb.SpringWebHttpServerDecorator.DD_HANDLER_SPAN_PREFIX_KEY;
4+
import static datadog.trace.instrumentation.springweb.SpringWebHttpServerDecorator.handlerSpanContinueKey;
5+
import static datadog.trace.instrumentation.springweb.SpringWebHttpServerDecorator.handlerSpanKey;
66

77
import com.google.auto.service.AutoService;
88
import datadog.trace.agent.tooling.Instrumenter;
@@ -53,12 +53,12 @@ public static void after(
5353
return;
5454
}
5555
ServletWebRequest servletWebRequest = (ServletWebRequest) nativeWebRequest;
56-
final String handlerSpanKey =
57-
DD_HANDLER_SPAN_PREFIX_KEY + self.getBean().getClass().getName();
56+
final Class<?> handlerClass = self.getBean().getClass();
57+
final String handlerSpanKey = handlerSpanKey(handlerClass);
5858

5959
if (Boolean.TRUE.equals(
6060
servletWebRequest.getAttribute(
61-
handlerSpanKey + DD_HANDLER_SPAN_CONTINUE_SUFFIX, ServletWebRequest.SCOPE_REQUEST))) {
61+
handlerSpanContinueKey(handlerClass), ServletWebRequest.SCOPE_REQUEST))) {
6262
return;
6363
}
6464

@@ -67,7 +67,7 @@ public static void after(
6767
return;
6868
}
6969
servletWebRequest.setAttribute(
70-
handlerSpanKey + DD_HANDLER_SPAN_CONTINUE_SUFFIX, true, ServletWebRequest.SCOPE_REQUEST);
70+
handlerSpanContinueKey(handlerClass), true, ServletWebRequest.SCOPE_REQUEST);
7171
result =
7272
((CompletionStage<?>) result)
7373
.whenComplete(AsyncResultExtensions.finishSpan((AgentSpan) span));

dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-3.1/src/main/java/datadog/trace/instrumentation/springweb/SpringWebHttpServerDecorator.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import static datadog.trace.bootstrap.instrumentation.decorator.http.HttpResourceDecorator.HTTP_RESOURCE_DECORATOR;
44

55
import datadog.context.Context;
6+
import datadog.trace.api.GenericClassValue;
67
import datadog.trace.bootstrap.instrumentation.api.AgentPropagation;
78
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
89
import datadog.trace.bootstrap.instrumentation.api.URIDataAdapter;
@@ -33,6 +34,21 @@ public class SpringWebHttpServerDecorator
3334
public static final String DD_HANDLER_SPAN_PREFIX_KEY = "dd.handler.span.";
3435
public static final String DD_HANDLER_SPAN_CONTINUE_SUFFIX = ".continue";
3536

37+
private static final ClassValue<String> HANDLER_SPAN_KEY_CACHE =
38+
GenericClassValue.of(type -> DD_HANDLER_SPAN_PREFIX_KEY + type.getName());
39+
40+
private static final ClassValue<String> HANDLER_SPAN_CONTINUE_KEY_CACHE =
41+
GenericClassValue.of(
42+
type -> DD_HANDLER_SPAN_PREFIX_KEY + type.getName() + DD_HANDLER_SPAN_CONTINUE_SUFFIX);
43+
44+
public static String handlerSpanKey(Class<?> handlerClass) {
45+
return HANDLER_SPAN_KEY_CACHE.get(handlerClass);
46+
}
47+
48+
public static String handlerSpanContinueKey(Class<?> handlerClass) {
49+
return HANDLER_SPAN_CONTINUE_KEY_CACHE.get(handlerClass);
50+
}
51+
3652
public SpringWebHttpServerDecorator(CharSequence component) {
3753
this.component = component;
3854
}

dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/src/main/java17/datadog/trace/instrumentation/springweb6/ControllerAdvice.java

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
import static datadog.trace.bootstrap.instrumentation.api.Java8BytecodeBridge.getCurrentContext;
77
import static datadog.trace.bootstrap.instrumentation.api.Java8BytecodeBridge.spanFromContext;
88
import static datadog.trace.bootstrap.instrumentation.decorator.HttpServerDecorator.DD_CONTEXT_ATTRIBUTE;
9-
import static datadog.trace.instrumentation.springweb6.SpringWebHttpServerDecorator.DD_HANDLER_SPAN_CONTINUE_SUFFIX;
10-
import static datadog.trace.instrumentation.springweb6.SpringWebHttpServerDecorator.DD_HANDLER_SPAN_PREFIX_KEY;
119
import static datadog.trace.instrumentation.springweb6.SpringWebHttpServerDecorator.DECORATE;
10+
import static datadog.trace.instrumentation.springweb6.SpringWebHttpServerDecorator.handlerSpanContinueKey;
11+
import static datadog.trace.instrumentation.springweb6.SpringWebHttpServerDecorator.handlerSpanKey;
1212

1313
import datadog.context.Context;
1414
import datadog.context.ContextScope;
@@ -23,8 +23,8 @@ public class ControllerAdvice {
2323
public static ContextScope nameResourceAndStartSpan(
2424
@Advice.Argument(0) final HttpServletRequest request,
2525
@Advice.Argument(2) final Object handler,
26-
@Advice.Local("handlerSpanKey") String handlerSpanKey) {
27-
handlerSpanKey = "";
26+
@Advice.Local("handlerClass") Class handlerClass) {
27+
handlerClass = null;
2828
// Name the parent span based on the matching pattern
2929
Object contextObj = request.getAttribute(DD_CONTEXT_ATTRIBUTE);
3030
if (contextObj instanceof Context) {
@@ -41,13 +41,12 @@ public static ContextScope nameResourceAndStartSpan(
4141

4242
// Now create a span for handler/controller execution.
4343

44-
final String handlerKey;
4544
if (handler instanceof HandlerMethod) {
46-
handlerKey = ((HandlerMethod) handler).getBean().getClass().getName();
45+
handlerClass = ((HandlerMethod) handler).getBean().getClass();
4746
} else {
48-
handlerKey = handler.getClass().getName();
47+
handlerClass = handler.getClass();
4948
}
50-
handlerSpanKey = DD_HANDLER_SPAN_PREFIX_KEY + handlerKey;
49+
final String handlerSpanKey = handlerSpanKey(handlerClass);
5150

5251
// If the context already exists, return it
5352
final Object existingContext = request.getAttribute(handlerSpanKey);
@@ -68,13 +67,12 @@ public static void stopSpan(
6867
@Advice.Enter final ContextScope scope,
6968
@Advice.Argument(0) final HttpServletRequest request,
7069
@Advice.Thrown final Throwable throwable,
71-
@Advice.Local("handlerSpanKey") String handlerSpanKey) {
70+
@Advice.Local("handlerClass") Class handlerClass) {
7271
if (scope == null) {
7372
return;
7473
}
7574
boolean finish =
76-
!Boolean.TRUE.equals(
77-
request.getAttribute(handlerSpanKey + DD_HANDLER_SPAN_CONTINUE_SUFFIX));
75+
!Boolean.TRUE.equals(request.getAttribute(handlerSpanContinueKey(handlerClass)));
7876
final AgentSpan span = spanFromContext(scope.context());
7977
scope.close();
8078
if (throwable != null) {

dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/src/main/java17/datadog/trace/instrumentation/springweb6/SpringWebHttpServerDecorator.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import static datadog.trace.bootstrap.instrumentation.decorator.http.HttpResourceDecorator.HTTP_RESOURCE_DECORATOR;
44

55
import datadog.context.Context;
6+
import datadog.trace.api.GenericClassValue;
67
import datadog.trace.bootstrap.instrumentation.api.AgentPropagation;
78
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
89
import datadog.trace.bootstrap.instrumentation.api.URIDataAdapter;
@@ -37,6 +38,21 @@ public class SpringWebHttpServerDecorator
3738
public static final String DD_HANDLER_SPAN_PREFIX_KEY = "dd.handler.span.";
3839
public static final String DD_HANDLER_SPAN_CONTINUE_SUFFIX = ".continue";
3940

41+
private static final ClassValue<String> HANDLER_SPAN_KEY_CACHE =
42+
GenericClassValue.of(type -> DD_HANDLER_SPAN_PREFIX_KEY + type.getName());
43+
44+
private static final ClassValue<String> HANDLER_SPAN_CONTINUE_KEY_CACHE =
45+
GenericClassValue.of(
46+
type -> DD_HANDLER_SPAN_PREFIX_KEY + type.getName() + DD_HANDLER_SPAN_CONTINUE_SUFFIX);
47+
48+
public static String handlerSpanKey(Class<?> handlerClass) {
49+
return HANDLER_SPAN_KEY_CACHE.get(handlerClass);
50+
}
51+
52+
public static String handlerSpanContinueKey(Class<?> handlerClass) {
53+
return HANDLER_SPAN_CONTINUE_KEY_CACHE.get(handlerClass);
54+
}
55+
4056
public SpringWebHttpServerDecorator(CharSequence component) {
4157
this.component = component;
4258
}

dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/src/main/java17/datadog/trace/instrumentation/springweb6/WrapContinuableResultAdvice.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package datadog.trace.instrumentation.springweb6;
22

3-
import static datadog.trace.instrumentation.springweb6.SpringWebHttpServerDecorator.DD_HANDLER_SPAN_CONTINUE_SUFFIX;
4-
import static datadog.trace.instrumentation.springweb6.SpringWebHttpServerDecorator.DD_HANDLER_SPAN_PREFIX_KEY;
3+
import static datadog.trace.instrumentation.springweb6.SpringWebHttpServerDecorator.handlerSpanContinueKey;
4+
import static datadog.trace.instrumentation.springweb6.SpringWebHttpServerDecorator.handlerSpanKey;
55

66
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
77
import datadog.trace.bootstrap.instrumentation.java.concurrent.AsyncResultExtensions;
@@ -24,19 +24,20 @@ public static void after(
2424
}
2525

2626
ServletWebRequest servletWebRequest = (ServletWebRequest) nativeWebRequest;
27-
final String handlerSpanKey = DD_HANDLER_SPAN_PREFIX_KEY + self.getBean().getClass().getName();
27+
final Class<?> handlerClass = self.getBean().getClass();
28+
final String handlerSpanKey = handlerSpanKey(handlerClass);
2829

2930
if (Boolean.TRUE.equals(
3031
servletWebRequest.getAttribute(
31-
handlerSpanKey + DD_HANDLER_SPAN_CONTINUE_SUFFIX, ServletWebRequest.SCOPE_REQUEST))) {
32+
handlerSpanContinueKey(handlerClass), ServletWebRequest.SCOPE_REQUEST))) {
3233
return;
3334
}
3435
Object span = servletWebRequest.getAttribute(handlerSpanKey, ServletWebRequest.SCOPE_REQUEST);
3536
if (!(span instanceof AgentSpan)) {
3637
return;
3738
}
3839
servletWebRequest.setAttribute(
39-
handlerSpanKey + DD_HANDLER_SPAN_CONTINUE_SUFFIX, true, ServletWebRequest.SCOPE_REQUEST);
40+
handlerSpanContinueKey(handlerClass), true, ServletWebRequest.SCOPE_REQUEST);
4041
result =
4142
((CompletionStage<?>) result)
4243
.whenComplete(AsyncResultExtensions.finishSpan((AgentSpan) span));

0 commit comments

Comments
 (0)