2121
2222import io .micrometer .observation .Observation ;
2323import io .micrometer .observation .ObservationRegistry ;
24+ import jakarta .servlet .AsyncEvent ;
25+ import jakarta .servlet .AsyncListener ;
2426import jakarta .servlet .FilterChain ;
2527import jakarta .servlet .RequestDispatcher ;
2628import jakarta .servlet .ServletException ;
29+ import jakarta .servlet .ServletRequest ;
2730import jakarta .servlet .http .HttpServletRequest ;
2831import jakarta .servlet .http .HttpServletResponse ;
2932
@@ -94,11 +97,6 @@ public static Optional<ServerRequestObservationContext> findObservationContext(H
9497 return Optional .ofNullable ((ServerRequestObservationContext ) request .getAttribute (CURRENT_OBSERVATION_CONTEXT_ATTRIBUTE ));
9598 }
9699
97- @ Override
98- protected boolean shouldNotFilterAsyncDispatch () {
99- return false ;
100- }
101-
102100 @ Override
103101 @ SuppressWarnings ("try" )
104102 protected void doFilterInternal (HttpServletRequest request , HttpServletResponse response , FilterChain filterChain )
@@ -115,8 +113,12 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
115113 throw ex ;
116114 }
117115 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 {
120122 Throwable error = fetchException (request );
121123 if (error != null ) {
122124 observation .error (error );
@@ -152,13 +154,43 @@ private Observation createOrFetchObservation(HttpServletRequest request, HttpSer
152154 }
153155
154156 @ Nullable
155- private Throwable unwrapServletException (Throwable ex ) {
157+ static Throwable unwrapServletException (Throwable ex ) {
156158 return (ex instanceof ServletException ) ? ex .getCause () : ex ;
157159 }
158160
159161 @ Nullable
160- private Throwable fetchException (HttpServletRequest request ) {
162+ static Throwable fetchException (ServletRequest request ) {
161163 return (Throwable ) request .getAttribute (RequestDispatcher .ERROR_EXCEPTION );
162164 }
163165
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+
164196}
0 commit comments