Skip to content

Commit 72f2be0

Browse files
committed
servlet-3.0
1 parent e1ce7ce commit 72f2be0

File tree

6 files changed

+149
-108
lines changed

6 files changed

+149
-108
lines changed

instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/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-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3Advice.java

Lines changed: 87 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -17,31 +17,95 @@
1717
import io.opentelemetry.javaagent.bootstrap.servlet.MappingResolver;
1818
import io.opentelemetry.javaagent.instrumentation.servlet.ServletRequestContext;
1919
import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.snippet.Servlet3SnippetInjectingResponseWrapper;
20+
import javax.annotation.Nullable;
2021
import javax.servlet.Servlet;
2122
import javax.servlet.ServletRequest;
2223
import javax.servlet.ServletResponse;
2324
import javax.servlet.http.HttpServletRequest;
2425
import javax.servlet.http.HttpServletResponse;
2526
import net.bytebuddy.asm.Advice;
27+
import net.bytebuddy.asm.Advice.AssignReturned;
28+
import net.bytebuddy.asm.Advice.AssignReturned.ToArguments.ToArgument;
2629
import net.bytebuddy.implementation.bytecode.assign.Assigner;
2730

2831
@SuppressWarnings("unused")
2932
public class Servlet3Advice {
3033

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

41106
if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
42-
return;
107+
return new Object[] {null, request, response};
43108
}
44-
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
45109

46110
String snippet = getSnippetInjectionHelper().getSnippet();
47111
if (!snippet.isEmpty()
@@ -50,70 +114,27 @@ public static void onEnter(
50114
response =
51115
new Servlet3SnippetInjectingResponseWrapper((HttpServletResponse) response, snippet);
52116
}
53-
callDepth = CallDepth.forClass(AppServerBridge.getCallDepthKey());
54-
callDepth.getAndIncrement();
55-
56-
Context currentContext = Java8BytecodeBridge.currentContext();
57-
Context attachedContext = helper().getServerContext(httpServletRequest);
58-
Context contextToUpdate;
59-
60-
requestContext = new ServletRequestContext<>(httpServletRequest, servletOrFilter);
61-
if (attachedContext == null && helper().shouldStart(currentContext, requestContext)) {
62-
context = helper().start(currentContext, requestContext);
63-
helper().setAsyncListenerResponse(context, (HttpServletResponse) response);
64-
65-
contextToUpdate = context;
66-
} else if (attachedContext != null
67-
&& helper().needsRescoping(currentContext, attachedContext)) {
68-
// Given request already has a context associated with it.
69-
// see the needsRescoping() javadoc for more explanation
70-
contextToUpdate = attachedContext;
71-
} else {
72-
// We are inside nested servlet/filter/app-server span, don't create new span
73-
contextToUpdate = currentContext;
74-
}
75-
76-
// Update context with info from current request to ensure that server span gets the best
77-
// possible name.
78-
// In case server span was created by app server instrumentations calling updateContext
79-
// returns a new context that contains servlet context path that is used in other
80-
// instrumentations for naming server span.
81-
MappingResolver mappingResolver = Servlet3Singletons.getMappingResolver(servletOrFilter);
82-
boolean servlet = servletOrFilter instanceof Servlet;
83-
contextToUpdate =
84-
helper().updateContext(contextToUpdate, httpServletRequest, mappingResolver, servlet);
85-
scope = contextToUpdate.makeCurrent();
86-
87-
if (context != null) {
88-
// Only trigger response customizer once, so only if server span was created here
89-
HttpServerResponseCustomizerHolder.getCustomizer()
90-
.customize(contextToUpdate, (HttpServletResponse) response, Servlet3Accessor.INSTANCE);
91-
}
117+
AdviceScope adviceScope =
118+
new AdviceScope(
119+
CallDepth.forClass(AppServerBridge.getCallDepthKey()),
120+
(HttpServletRequest) request,
121+
(HttpServletResponse) response,
122+
servletOrFilter);
123+
return new Object[] {adviceScope, request, response};
92124
}
93125

94126
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
95127
public static void stopSpan(
96128
@Advice.Argument(0) ServletRequest request,
97129
@Advice.Argument(1) ServletResponse response,
98-
@Advice.Thrown Throwable throwable,
99-
@Advice.Local("otelCallDepth") CallDepth callDepth,
100-
@Advice.Local("otelRequest") ServletRequestContext<HttpServletRequest> requestContext,
101-
@Advice.Local("otelContext") Context context,
102-
@Advice.Local("otelScope") Scope scope) {
103-
104-
if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
130+
@Advice.Thrown @Nullable Throwable throwable,
131+
@Advice.Enter Object[] enterResult) {
132+
AdviceScope adviceScope = (AdviceScope) enterResult[0];
133+
if (adviceScope == null
134+
|| !(request instanceof HttpServletRequest)
135+
|| !(response instanceof HttpServletResponse)) {
105136
return;
106137
}
107-
108-
boolean topLevel = callDepth.decrementAndGet() == 0;
109-
helper()
110-
.end(
111-
requestContext,
112-
(HttpServletRequest) request,
113-
(HttpServletResponse) response,
114-
throwable,
115-
topLevel,
116-
context,
117-
scope);
138+
adviceScope.exit(throwable, (HttpServletRequest) request, (HttpServletResponse) response);
118139
}
119140
}

instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3AsyncContextStartAdvice.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.v3_0.Servlet3Singletons.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 Servlet3AsyncContextStartAdvice {
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-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3AsyncStartAdvice.java

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

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

2728
@Advice.OnMethodExit(suppress = Throwable.class)
2829
public static void startAsyncExit(
29-
@Advice.This ServletRequest servletRequest,
30-
@Advice.Local("otelCallDepth") CallDepth callDepth) {
30+
@Advice.This ServletRequest servletRequest, @Advice.Enter CallDepth callDepth) {
3131

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

instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3InstrumentationModule.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,9 @@ BASE_PACKAGE, adviceClassName(".Servlet3AsyncContextStartAdvice")),
6565
private static String adviceClassName(String suffix) {
6666
return Servlet3InstrumentationModule.class.getPackage().getName() + suffix;
6767
}
68+
69+
@Override
70+
public boolean isIndyReady() {
71+
return true;
72+
}
6873
}

instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3ResponseSendAdvice.java

Lines changed: 42 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -12,47 +12,60 @@
1212
import io.opentelemetry.instrumentation.api.incubator.semconv.util.ClassAndMethod;
1313
import io.opentelemetry.javaagent.bootstrap.CallDepth;
1414
import io.opentelemetry.javaagent.instrumentation.servlet.common.response.HttpServletResponseAdviceHelper;
15+
import javax.annotation.Nullable;
1516
import javax.servlet.http.HttpServletResponse;
1617
import net.bytebuddy.asm.Advice;
1718

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

21-
@Advice.OnMethodEnter(suppress = Throwable.class)
22-
public static void start(
23-
@Advice.Origin("#t") Class<?> declaringClass,
24-
@Advice.Origin("#m") String methodName,
25-
@Advice.Local("otelMethod") ClassAndMethod classAndMethod,
26-
@Advice.Local("otelContext") Context context,
27-
@Advice.Local("otelScope") Scope scope,
28-
@Advice.Local("otelCallDepth") CallDepth callDepth) {
29-
callDepth = CallDepth.forClass(HttpServletResponse.class);
30-
if (callDepth.getAndIncrement() > 0) {
31-
return;
22+
public static class AdviceScope {
23+
private final CallDepth callDepth;
24+
private final ClassAndMethod classAndMethod;
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+
classAndMethod = result.getClassAndMethod();
41+
context = result.getContext();
42+
scope = result.getScope();
43+
} else {
44+
classAndMethod = null;
45+
context = null;
46+
scope = null;
47+
}
3248
}
3349

34-
HttpServletResponseAdviceHelper.StartResult result =
35-
HttpServletResponseAdviceHelper.startSpan(
36-
responseInstrumenter(), declaringClass, methodName);
37-
if (result != null) {
38-
classAndMethod = result.getClassAndMethod();
39-
context = result.getContext();
40-
scope = result.getScope();
50+
public void exit(@Nullable Throwable throwable) {
51+
if (callDepth.decrementAndGet() > 0) {
52+
return;
53+
}
54+
HttpServletResponseAdviceHelper.stopSpan(
55+
responseInstrumenter(), throwable, context, scope, classAndMethod);
4156
}
4257
}
4358

59+
@Advice.OnMethodEnter(suppress = Throwable.class)
60+
public static AdviceScope start(
61+
@Advice.Origin("#t") Class<?> declaringClass, @Advice.Origin("#m") String methodName) {
62+
return new AdviceScope(
63+
CallDepth.forClass(HttpServletResponse.class), declaringClass, methodName);
64+
}
65+
4466
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
4567
public static void stopSpan(
46-
@Advice.Thrown Throwable throwable,
47-
@Advice.Local("otelMethod") ClassAndMethod classAndMethod,
48-
@Advice.Local("otelContext") Context context,
49-
@Advice.Local("otelScope") Scope scope,
50-
@Advice.Local("otelCallDepth") CallDepth callDepth) {
51-
if (callDepth.decrementAndGet() > 0) {
52-
return;
53-
}
54-
55-
HttpServletResponseAdviceHelper.stopSpan(
56-
responseInstrumenter(), throwable, context, scope, classAndMethod);
68+
@Advice.Thrown Throwable throwable, @Advice.Enter AdviceScope adviceScope) {
69+
adviceScope.exit(throwable);
5770
}
5871
}

0 commit comments

Comments
 (0)