Skip to content

Commit e4baf55

Browse files
committed
servlet-5.0
1 parent 72f2be0 commit e4baf55

File tree

6 files changed

+163
-104
lines changed

6 files changed

+163
-104
lines changed

instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/JakartaServletInstrumentationModule.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.google.auto.service.AutoService;
99
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
1010
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
11+
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
1112
import io.opentelemetry.javaagent.instrumentation.servlet.common.async.AsyncContextInstrumentation;
1213
import io.opentelemetry.javaagent.instrumentation.servlet.common.async.AsyncContextStartInstrumentation;
1314
import io.opentelemetry.javaagent.instrumentation.servlet.common.async.AsyncStartInstrumentation;
@@ -18,7 +19,8 @@
1819
import java.util.List;
1920

2021
@AutoService(InstrumentationModule.class)
21-
public class JakartaServletInstrumentationModule extends InstrumentationModule {
22+
public class JakartaServletInstrumentationModule extends InstrumentationModule
23+
implements ExperimentalInstrumentationModule {
2224
private static final String BASE_PACKAGE = "jakarta.servlet";
2325

2426
public JakartaServletInstrumentationModule() {
@@ -50,4 +52,9 @@ BASE_PACKAGE, adviceClassName(".async.AsyncContextStartAdvice")),
5052
private static String adviceClassName(String suffix) {
5153
return JakartaServletInstrumentationModule.class.getPackage().getName() + suffix;
5254
}
55+
56+
@Override
57+
public boolean isIndyReady() {
58+
return true;
59+
}
5360
}

instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/async/AsyncContextStartAdvice.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@
88
import static io.opentelemetry.javaagent.instrumentation.servlet.v5_0.Servlet5Singletons.helper;
99

1010
import net.bytebuddy.asm.Advice;
11+
import net.bytebuddy.asm.Advice.AssignReturned;
12+
import net.bytebuddy.asm.Advice.AssignReturned.ToArguments.ToArgument;
1113

1214
@SuppressWarnings("unused")
1315
public class AsyncContextStartAdvice {
1416

17+
@AssignReturned.ToArguments(@ToArgument(0))
1518
@Advice.OnMethodEnter(suppress = Throwable.class)
16-
public static void start(@Advice.Argument(value = 0, readOnly = false) Runnable runnable) {
17-
runnable = helper().wrapAsyncRunnable(runnable);
19+
public static Runnable start(@Advice.Argument(0) Runnable runnable) {
20+
return helper().wrapAsyncRunnable(runnable);
1821
}
1922
}

instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/async/AsyncDispatchAdvice.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,11 @@
1919
public class AsyncDispatchAdvice {
2020

2121
@Advice.OnMethodEnter(suppress = Throwable.class)
22-
public static void enter(
23-
@Advice.This AsyncContext context,
24-
@Advice.AllArguments Object[] args,
25-
@Advice.Local("otelCallDepth") CallDepth callDepth) {
26-
callDepth = CallDepth.forClass(AsyncContext.class);
22+
public static CallDepth enter(
23+
@Advice.This AsyncContext context, @Advice.AllArguments Object[] args) {
24+
CallDepth callDepth = CallDepth.forClass(AsyncContext.class);
2725
if (callDepth.getAndIncrement() > 0) {
28-
return;
26+
return callDepth;
2927
}
3028

3129
ServletRequest request = context.getRequest();
@@ -42,10 +40,11 @@ public static void enter(
4240
// processing, and nothing can be done with the request anymore after this
4341
request.setAttribute(CONTEXT_ATTRIBUTE, currentContext);
4442
}
43+
return callDepth;
4544
}
4645

4746
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
48-
public static void exit(@Advice.Local("otelCallDepth") CallDepth callDepth) {
47+
public static void exit(@Advice.Enter CallDepth callDepth) {
4948
callDepth.decrementAndGet();
5049
}
5150
}

instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/async/AsyncStartAdvice.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,17 @@
1818
public class AsyncStartAdvice {
1919

2020
@Advice.OnMethodEnter(suppress = Throwable.class)
21-
public static void startAsyncEnter(@Advice.Local("otelCallDepth") CallDepth callDepth) {
21+
public static CallDepth startAsyncEnter() {
2222
// This allows to detect the outermost invocation of startAsync in method exit
23-
callDepth = CallDepth.forClass(AsyncContext.class);
23+
CallDepth callDepth = CallDepth.forClass(AsyncContext.class);
2424
callDepth.getAndIncrement();
25+
return callDepth;
2526
}
2627

2728
@Advice.OnMethodExit(suppress = Throwable.class)
2829
public static void startAsyncExit(
2930
@Advice.This(typing = Assigner.Typing.DYNAMIC) HttpServletRequest request,
30-
@Advice.Local("otelCallDepth") CallDepth callDepth) {
31+
@Advice.Enter CallDepth callDepth) {
3132

3233
if (callDepth.decrementAndGet() != 0) {
3334
// This is not the outermost invocation, ignore.

instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/response/ResponseSendAdvice.java

Lines changed: 53 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -13,47 +13,71 @@
1313
import io.opentelemetry.javaagent.bootstrap.CallDepth;
1414
import io.opentelemetry.javaagent.instrumentation.servlet.common.response.HttpServletResponseAdviceHelper;
1515
import jakarta.servlet.http.HttpServletResponse;
16+
import javax.annotation.Nullable;
1617
import net.bytebuddy.asm.Advice;
1718

1819
@SuppressWarnings("unused")
1920
public class ResponseSendAdvice {
2021

22+
public static class AdviceScope {
23+
private final ClassAndMethod classAndMethod;
24+
private final CallDepth callDepth;
25+
private final Context context;
26+
private final Scope scope;
27+
28+
public AdviceScope(CallDepth callDepth, Class<?> declaringClass, String methodName) {
29+
this.callDepth = callDepth;
30+
if (callDepth.getAndIncrement() > 0) {
31+
this.classAndMethod = null;
32+
this.context = null;
33+
this.scope = null;
34+
return;
35+
}
36+
HttpServletResponseAdviceHelper.StartResult result =
37+
HttpServletResponseAdviceHelper.startSpan(
38+
responseInstrumenter(), declaringClass, methodName);
39+
if (result == null) {
40+
this.classAndMethod = null;
41+
this.context = null;
42+
this.scope = null;
43+
return;
44+
}
45+
this.classAndMethod = result.getClassAndMethod();
46+
this.context = result.getContext();
47+
this.scope = result.getScope();
48+
}
49+
50+
public AdviceScope(
51+
CallDepth callDepth, HttpServletResponseAdviceHelper.StartResult startResult) {
52+
this.callDepth = callDepth;
53+
this.classAndMethod = startResult.getClassAndMethod();
54+
this.context = startResult.getContext();
55+
this.scope = startResult.getScope();
56+
}
57+
58+
public void exit(@Nullable Throwable throwable) {
59+
if (callDepth.decrementAndGet() > 0) {
60+
return;
61+
}
62+
HttpServletResponseAdviceHelper.stopSpan(
63+
responseInstrumenter(), throwable, context, scope, classAndMethod);
64+
}
65+
}
66+
67+
@Nullable
2168
@Advice.OnMethodEnter(suppress = Throwable.class)
22-
public static void start(
69+
public static AdviceScope start(
2370
@Advice.This Object response,
2471
@Advice.Origin("#t") Class<?> declaringClass,
25-
@Advice.Origin("#m") String methodName,
26-
@Advice.Local("otelMethod") ClassAndMethod classAndMethod,
27-
@Advice.Local("otelContext") Context context,
28-
@Advice.Local("otelScope") Scope scope,
29-
@Advice.Local("otelCallDepth") CallDepth callDepth) {
30-
callDepth = CallDepth.forClass(HttpServletResponse.class);
31-
if (callDepth.getAndIncrement() > 0) {
32-
return;
33-
}
72+
@Advice.Origin("#m") String methodName) {
3473

35-
HttpServletResponseAdviceHelper.StartResult result =
36-
HttpServletResponseAdviceHelper.startSpan(
37-
responseInstrumenter(), declaringClass, methodName);
38-
if (result != null) {
39-
classAndMethod = result.getClassAndMethod();
40-
context = result.getContext();
41-
scope = result.getScope();
42-
}
74+
CallDepth callDepth = CallDepth.forClass(HttpServletResponse.class);
75+
return new AdviceScope(callDepth, declaringClass, methodName);
4376
}
4477

4578
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
4679
public static void stopSpan(
47-
@Advice.Thrown Throwable throwable,
48-
@Advice.Local("otelMethod") ClassAndMethod classAndMethod,
49-
@Advice.Local("otelContext") Context context,
50-
@Advice.Local("otelScope") Scope scope,
51-
@Advice.Local("otelCallDepth") CallDepth callDepth) {
52-
if (callDepth.decrementAndGet() > 0) {
53-
return;
54-
}
55-
56-
HttpServletResponseAdviceHelper.stopSpan(
57-
responseInstrumenter(), throwable, context, scope, classAndMethod);
80+
@Advice.Thrown @Nullable Throwable throwable, @Advice.Enter AdviceScope adviceScope) {
81+
adviceScope.exit(throwable);
5882
}
5983
}

instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/service/JakartaServletServiceAdvice.java

Lines changed: 87 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,89 @@
2424
import jakarta.servlet.ServletResponse;
2525
import jakarta.servlet.http.HttpServletRequest;
2626
import jakarta.servlet.http.HttpServletResponse;
27+
import javax.annotation.Nullable;
2728
import net.bytebuddy.asm.Advice;
29+
import net.bytebuddy.asm.Advice.AssignReturned;
30+
import net.bytebuddy.asm.Advice.AssignReturned.ToArguments.ToArgument;
2831
import net.bytebuddy.implementation.bytecode.assign.Assigner;
2932

3033
@SuppressWarnings("unused")
3134
public class JakartaServletServiceAdvice {
3235

36+
public static class AdviceScope {
37+
private final CallDepth callDepth;
38+
private final ServletRequestContext<HttpServletRequest> requestContext;
39+
private final Context context;
40+
private final Scope scope;
41+
42+
public AdviceScope(
43+
CallDepth callDepth,
44+
Object servletOrFilter,
45+
HttpServletRequest request,
46+
ServletResponse response) {
47+
this.callDepth = callDepth;
48+
this.callDepth.getAndIncrement();
49+
50+
Context currentContext = Java8BytecodeBridge.currentContext();
51+
Context attachedContext = helper().getServerContext(request);
52+
Context contextToUpdate;
53+
54+
requestContext = new ServletRequestContext<>(request, servletOrFilter);
55+
if (attachedContext == null && helper().shouldStart(currentContext, requestContext)) {
56+
context = helper().start(currentContext, requestContext);
57+
helper().setAsyncListenerResponse(context, (HttpServletResponse) response);
58+
59+
contextToUpdate = context;
60+
} else if (attachedContext != null
61+
&& helper().needsRescoping(currentContext, attachedContext)) {
62+
// Given request already has a context associated with it.
63+
// see the needsRescoping() javadoc for more explanation
64+
contextToUpdate = attachedContext;
65+
context = null;
66+
} else {
67+
// We are inside nested servlet/filter/app-server span, don't create new span
68+
contextToUpdate = currentContext;
69+
context = null;
70+
}
71+
72+
// Update context with info from current request to ensure that server span gets the best
73+
// possible name.
74+
// In case server span was created by app server instrumentations calling updateContext
75+
// returns a new context that contains servlet context path that is used in other
76+
// instrumentations for naming server span.
77+
MappingResolver mappingResolver = Servlet5Singletons.getMappingResolver(servletOrFilter);
78+
boolean servlet = servletOrFilter instanceof Servlet;
79+
contextToUpdate = helper().updateContext(contextToUpdate, request, mappingResolver, servlet);
80+
scope = contextToUpdate.makeCurrent();
81+
82+
if (context != null) {
83+
// Only trigger response customizer once, so only if server span was created here
84+
HttpServerResponseCustomizerHolder.getCustomizer()
85+
.customize(contextToUpdate, (HttpServletResponse) response, Servlet5Accessor.INSTANCE);
86+
}
87+
}
88+
89+
public void exit(
90+
HttpServletRequest request, HttpServletResponse response, @Nullable Throwable throwable) {
91+
boolean topLevel = callDepth.decrementAndGet() == 0;
92+
helper().end(requestContext, request, response, throwable, topLevel, context, scope);
93+
}
94+
}
95+
96+
@AssignReturned.ToArguments({
97+
@ToArgument(value = 0, index = 1),
98+
@ToArgument(value = 1, index = 2)
99+
})
33100
@Advice.OnMethodEnter(suppress = Throwable.class)
34-
public static void onEnter(
101+
public static Object[] onEnter(
35102
@Advice.This(typing = Assigner.Typing.DYNAMIC) Object servletOrFilter,
36-
@Advice.Argument(value = 0, readOnly = false) ServletRequest request,
37-
@Advice.Argument(value = 1, readOnly = false) ServletResponse response,
38-
@Advice.Local("otelCallDepth") CallDepth callDepth,
39-
@Advice.Local("otelRequest") ServletRequestContext<HttpServletRequest> requestContext,
40-
@Advice.Local("otelContext") Context context,
41-
@Advice.Local("otelScope") Scope scope) {
103+
@Advice.Argument(0) ServletRequest request,
104+
@Advice.Argument(1) ServletResponse originalResponse) {
105+
106+
ServletResponse response = originalResponse;
42107

43108
if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
44-
return;
109+
return new Object[] {null, request, response};
45110
}
46111
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
47112

@@ -53,71 +118,31 @@ public static void onEnter(
53118
new Servlet5SnippetInjectingResponseWrapper((HttpServletResponse) response, snippet);
54119
}
55120

56-
callDepth = CallDepth.forClass(AppServerBridge.getCallDepthKey());
57-
callDepth.getAndIncrement();
58-
59-
Context currentContext = Java8BytecodeBridge.currentContext();
60-
Context attachedContext = helper().getServerContext(httpServletRequest);
61-
Context contextToUpdate;
62-
63-
requestContext = new ServletRequestContext<>(httpServletRequest, servletOrFilter);
64-
if (attachedContext == null && helper().shouldStart(currentContext, requestContext)) {
65-
context = helper().start(currentContext, requestContext);
66-
helper().setAsyncListenerResponse(context, (HttpServletResponse) response);
67-
68-
contextToUpdate = context;
69-
} else if (attachedContext != null
70-
&& helper().needsRescoping(currentContext, attachedContext)) {
71-
// Given request already has a context associated with it.
72-
// see the needsRescoping() javadoc for more explanation
73-
contextToUpdate = attachedContext;
74-
} else {
75-
// We are inside nested servlet/filter/app-server span, don't create new span
76-
contextToUpdate = currentContext;
77-
}
121+
AdviceScope adviceScope =
122+
new AdviceScope(
123+
CallDepth.forClass(AppServerBridge.getCallDepthKey()),
124+
servletOrFilter,
125+
(HttpServletRequest) request,
126+
response);
78127

79-
// Update context with info from current request to ensure that server span gets the best
80-
// possible name.
81-
// In case server span was created by app server instrumentations calling updateContext
82-
// returns a new context that contains servlet context path that is used in other
83-
// instrumentations for naming server span.
84-
MappingResolver mappingResolver = Servlet5Singletons.getMappingResolver(servletOrFilter);
85-
boolean servlet = servletOrFilter instanceof Servlet;
86-
contextToUpdate =
87-
helper().updateContext(contextToUpdate, httpServletRequest, mappingResolver, servlet);
88-
scope = contextToUpdate.makeCurrent();
89-
90-
if (context != null) {
91-
// Only trigger response customizer once, so only if server span was created here
92-
HttpServerResponseCustomizerHolder.getCustomizer()
93-
.customize(contextToUpdate, (HttpServletResponse) response, Servlet5Accessor.INSTANCE);
94-
}
128+
return new Object[] {adviceScope, request, response};
95129
}
96130

97131
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
98132
public static void stopSpan(
99133
@Advice.Argument(0) ServletRequest request,
100134
@Advice.Argument(1) ServletResponse response,
101135
@Advice.Thrown Throwable throwable,
102-
@Advice.Local("otelCallDepth") CallDepth callDepth,
103-
@Advice.Local("otelRequest") ServletRequestContext<HttpServletRequest> requestContext,
104-
@Advice.Local("otelContext") Context context,
105-
@Advice.Local("otelScope") Scope scope) {
136+
@Advice.Enter Object[] enterResult) {
106137

107-
if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
138+
AdviceScope adviceScope = (AdviceScope) enterResult[0];
139+
140+
if (adviceScope == null
141+
|| !(request instanceof HttpServletRequest)
142+
|| !(response instanceof HttpServletResponse)) {
108143
return;
109144
}
110145

111-
boolean topLevel = callDepth.decrementAndGet() == 0;
112-
113-
helper()
114-
.end(
115-
requestContext,
116-
(HttpServletRequest) request,
117-
(HttpServletResponse) response,
118-
throwable,
119-
topLevel,
120-
context,
121-
scope);
146+
adviceScope.exit((HttpServletRequest) request, (HttpServletResponse) response, throwable);
122147
}
123148
}

0 commit comments

Comments
 (0)