21
21
22
22
import io .micrometer .observation .Observation ;
23
23
import io .micrometer .observation .ObservationRegistry ;
24
+ import jakarta .servlet .AsyncEvent ;
25
+ import jakarta .servlet .AsyncListener ;
24
26
import jakarta .servlet .FilterChain ;
25
27
import jakarta .servlet .RequestDispatcher ;
26
28
import jakarta .servlet .ServletException ;
29
+ import jakarta .servlet .ServletRequest ;
27
30
import jakarta .servlet .http .HttpServletRequest ;
28
31
import jakarta .servlet .http .HttpServletResponse ;
29
32
@@ -94,11 +97,6 @@ public static Optional<ServerRequestObservationContext> findObservationContext(H
94
97
return Optional .ofNullable ((ServerRequestObservationContext ) request .getAttribute (CURRENT_OBSERVATION_CONTEXT_ATTRIBUTE ));
95
98
}
96
99
97
- @ Override
98
- protected boolean shouldNotFilterAsyncDispatch () {
99
- return false ;
100
- }
101
-
102
100
@ Override
103
101
@ SuppressWarnings ("try" )
104
102
protected void doFilterInternal (HttpServletRequest request , HttpServletResponse response , FilterChain filterChain )
@@ -115,8 +113,12 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
115
113
throw ex ;
116
114
}
117
115
finally {
118
- // Only stop Observation if async processing is done or has never been started.
119
- if (!request .isAsyncStarted ()) {
116
+ // If async is started, register a listener for completion notification.
117
+ if (request .isAsyncStarted ()) {
118
+ request .getAsyncContext ().addListener (new ObservationAsyncListener (observation ));
119
+ }
120
+ // Stop Observation right now if async processing has not been started.
121
+ else {
120
122
Throwable error = fetchException (request );
121
123
if (error != null ) {
122
124
observation .error (error );
@@ -152,13 +154,43 @@ private Observation createOrFetchObservation(HttpServletRequest request, HttpSer
152
154
}
153
155
154
156
@ Nullable
155
- private Throwable unwrapServletException (Throwable ex ) {
157
+ static Throwable unwrapServletException (Throwable ex ) {
156
158
return (ex instanceof ServletException ) ? ex .getCause () : ex ;
157
159
}
158
160
159
161
@ Nullable
160
- private Throwable fetchException (HttpServletRequest request ) {
162
+ static Throwable fetchException (ServletRequest request ) {
161
163
return (Throwable ) request .getAttribute (RequestDispatcher .ERROR_EXCEPTION );
162
164
}
163
165
166
+ private static class ObservationAsyncListener implements AsyncListener {
167
+
168
+ private final Observation currentObservation ;
169
+
170
+ public ObservationAsyncListener (Observation currentObservation ) {
171
+ this .currentObservation = currentObservation ;
172
+ }
173
+
174
+ @ Override
175
+ public void onStartAsync (AsyncEvent event ) {
176
+ }
177
+
178
+ @ Override
179
+ public void onTimeout (AsyncEvent event ) {
180
+ this .currentObservation .stop ();
181
+ }
182
+
183
+ @ Override
184
+ public void onComplete (AsyncEvent event ) {
185
+ this .currentObservation .stop ();
186
+ }
187
+
188
+ @ Override
189
+ public void onError (AsyncEvent event ) {
190
+ this .currentObservation .error (unwrapServletException (event .getThrowable ()));
191
+ this .currentObservation .stop ();
192
+ }
193
+
194
+ }
195
+
164
196
}
0 commit comments