16
16
import java .lang .invoke .MethodHandles ;
17
17
import java .lang .invoke .MethodType ;
18
18
import java .util .ArrayList ;
19
+ import java .util .HashMap ;
19
20
import java .util .List ;
21
+ import java .util .Map ;
20
22
import java .util .Objects ;
21
23
import javax .annotation .Nullable ;
22
24
import javax .servlet .Filter ;
26
28
import javax .servlet .ServletRequest ;
27
29
import javax .servlet .ServletResponse ;
28
30
import javax .servlet .http .HttpServletRequest ;
31
+ import javax .servlet .http .HttpServletRequestWrapper ;
29
32
import javax .servlet .http .HttpServletResponse ;
30
33
import org .springframework .core .Ordered ;
31
34
import org .springframework .web .servlet .HandlerExecutionChain ;
32
35
import org .springframework .web .servlet .HandlerMapping ;
33
36
import org .springframework .web .servlet .mvc .method .annotation .RequestMappingHandlerMapping ;
34
37
35
38
public class OpenTelemetryHandlerMappingFilter implements Filter , Ordered {
36
- private static final String PATH_ATTRIBUTE = getRequestPathAttribute ();
37
39
private static final MethodHandle usesPathPatternsMh = getUsesPathPatternsMh ();
38
40
private static final MethodHandle parseAndCacheMh = parseAndCacheMh ();
39
41
40
42
private final HttpServerRouteGetter <HttpServletRequest > serverSpanName =
41
43
(context , request ) -> {
42
- Object previousValue = null ;
43
- if (this .parseRequestPath && PATH_ATTRIBUTE != null ) {
44
- previousValue = request .getAttribute (PATH_ATTRIBUTE );
44
+ if (this .parseRequestPath ) {
45
45
// sets new value for PATH_ATTRIBUTE of request
46
46
parseAndCache (request );
47
47
}
48
- try {
49
- if (findMapping (request )) {
50
- // Name the parent span based on the matching pattern
51
- // Let the parent span resource name be set with the attribute set in findMapping.
52
- return SpringWebMvcServerSpanNaming .SERVER_SPAN_NAME .get (context , request );
53
- }
54
- } finally {
55
- // mimic spring DispatcherServlet and restore the previous value of PATH_ATTRIBUTE
56
- if (this .parseRequestPath && PATH_ATTRIBUTE != null ) {
57
- if (previousValue == null ) {
58
- request .removeAttribute (PATH_ATTRIBUTE );
59
- } else {
60
- request .setAttribute (PATH_ATTRIBUTE , previousValue );
61
- }
62
- }
48
+ if (findMapping (request )) {
49
+ // Name the parent span based on the matching pattern
50
+ // Let the parent span resource name be set with the attribute set in findMapping.
51
+ return SpringWebMvcServerSpanNaming .SERVER_SPAN_NAME .get (context , request );
63
52
}
53
+
64
54
return null ;
65
55
};
66
56
@@ -84,14 +74,39 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
84
74
} finally {
85
75
if (handlerMappings != null ) {
86
76
Context context = Context .current ();
87
- HttpServerRoute .update (context , CONTROLLER , serverSpanName , ( HttpServletRequest ) request );
77
+ HttpServerRoute .update (context , CONTROLLER , serverSpanName , prepareRequest ( request ) );
88
78
}
89
79
}
90
80
}
91
81
92
82
@ Override
93
83
public void destroy () {}
94
84
85
+ private static HttpServletRequest prepareRequest (ServletRequest request ) {
86
+ // https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/10379
87
+ // Finding the request handler modifies request attributes. We are wrapping the request to avoid
88
+ // this.
89
+ return new HttpServletRequestWrapper ((HttpServletRequest ) request ) {
90
+ private final Map <String , Object > attributes = new HashMap <>();
91
+
92
+ @ Override
93
+ public void setAttribute (String name , Object o ) {
94
+ attributes .put (name , o );
95
+ }
96
+
97
+ @ Override
98
+ public Object getAttribute (String name ) {
99
+ Object value = attributes .get (name );
100
+ return value != null ? value : super .getAttribute (name );
101
+ }
102
+
103
+ @ Override
104
+ public void removeAttribute (String name ) {
105
+ attributes .remove (name );
106
+ }
107
+ };
108
+ }
109
+
95
110
/**
96
111
* When a HandlerMapping matches a request, it sets HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE
97
112
* as an attribute on the request. This attribute is read by SpringWebMvcDecorator.onRequest and
@@ -186,19 +201,4 @@ private static void parseAndCache(HttpServletRequest request) {
186
201
throw new IllegalStateException (throwable );
187
202
}
188
203
}
189
-
190
- private static String getRequestPathAttribute () {
191
- try {
192
- Class <?> pathUtilsClass =
193
- Class .forName ("org.springframework.web.util.ServletRequestPathUtils" );
194
- return (String )
195
- MethodHandles .lookup ()
196
- .findStaticGetter (pathUtilsClass , "PATH_ATTRIBUTE" , String .class )
197
- .invoke ();
198
- } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException exception ) {
199
- return null ;
200
- } catch (Throwable throwable ) {
201
- throw new IllegalStateException (throwable );
202
- }
203
- }
204
204
}
0 commit comments