1616
1717package org .springframework .web .filter ;
1818
19+ import java .io .IOException ;
20+
1921import io .micrometer .observation .ObservationRegistry ;
2022import io .micrometer .observation .tck .TestObservationRegistry ;
2123import io .micrometer .observation .tck .TestObservationRegistryAssert ;
24+ import jakarta .servlet .AsyncEvent ;
25+ import jakarta .servlet .AsyncListener ;
26+ import jakarta .servlet .DispatcherType ;
2227import jakarta .servlet .RequestDispatcher ;
2328import jakarta .servlet .ServletException ;
29+ import jakarta .servlet .http .HttpServlet ;
30+ import jakarta .servlet .http .HttpServletRequest ;
31+ import jakarta .servlet .http .HttpServletResponse ;
2432import org .junit .jupiter .api .Test ;
2533
2634import org .springframework .http .HttpMethod ;
2735import org .springframework .http .server .observation .ServerRequestObservationContext ;
36+ import org .springframework .web .testfixture .servlet .MockAsyncContext ;
2837import org .springframework .web .testfixture .servlet .MockFilterChain ;
2938import org .springframework .web .testfixture .servlet .MockHttpServletRequest ;
3039import org .springframework .web .testfixture .servlet .MockHttpServletResponse ;
@@ -41,18 +50,18 @@ class ServerHttpObservationFilterTests {
4150
4251 private final TestObservationRegistry observationRegistry = TestObservationRegistry .create ();
4352
44- private final ServerHttpObservationFilter filter = new ServerHttpObservationFilter (this .observationRegistry );
45-
46- private final MockFilterChain mockFilterChain = new MockFilterChain ();
47-
4853 private final MockHttpServletRequest request = new MockHttpServletRequest (HttpMethod .GET .name (), "/resource/test" );
4954
5055 private final MockHttpServletResponse response = new MockHttpServletResponse ();
5156
57+ private MockFilterChain mockFilterChain = new MockFilterChain ();
58+
59+ private ServerHttpObservationFilter filter = new ServerHttpObservationFilter (this .observationRegistry );
60+
5261
5362 @ Test
54- void filterShouldNotProcessAsyncDispatch () {
55- assertThat (this .filter .shouldNotFilterAsyncDispatch ()).isTrue ();
63+ void filterShouldProcessAsyncDispatch () {
64+ assertThat (this .filter .shouldNotFilterAsyncDispatch ()).isFalse ();
5665 }
5766
5867 @ Test
@@ -68,6 +77,12 @@ void filterShouldFillObservationContext() throws Exception {
6877 assertThatHttpObservation ().hasLowCardinalityKeyValue ("outcome" , "SUCCESS" ).hasBeenStopped ();
6978 }
7079
80+ @ Test
81+ void filterShouldOpenScope () throws Exception {
82+ this .mockFilterChain = new MockFilterChain (new ScopeCheckingServlet (this .observationRegistry ));
83+ filter .doFilter (this .request , this .response , this .mockFilterChain );
84+ }
85+
7186 @ Test
7287 void filterShouldAcceptNoOpObservationContext () throws Exception {
7388 ServerHttpObservationFilter filter = new ServerHttpObservationFilter (ObservationRegistry .NOOP );
@@ -124,9 +139,52 @@ void shouldCloseObservationAfterAsyncCompletion() throws Exception {
124139 assertThatHttpObservation ().hasLowCardinalityKeyValue ("outcome" , "SUCCESS" ).hasBeenStopped ();
125140 }
126141
142+ @ Test
143+ void shouldCloseObservationAfterAsyncError () throws Exception {
144+ this .request .setAsyncSupported (true );
145+ this .request .startAsync ();
146+ this .filter .doFilter (this .request , this .response , this .mockFilterChain );
147+ MockAsyncContext asyncContext = (MockAsyncContext ) this .request .getAsyncContext ();
148+ for (AsyncListener listener : asyncContext .getListeners ()) {
149+ listener .onError (new AsyncEvent (this .request .getAsyncContext (), new IllegalStateException ("test error" )));
150+ }
151+ asyncContext .complete ();
152+ assertThatHttpObservation ().hasLowCardinalityKeyValue ("exception" , "IllegalStateException" ).hasBeenStopped ();
153+ }
154+
155+ @ Test
156+ void shouldNotCloseObservationDuringAsyncDispatch () throws Exception {
157+ this .mockFilterChain = new MockFilterChain (new ScopeCheckingServlet (this .observationRegistry ));
158+ this .request .setDispatcherType (DispatcherType .ASYNC );
159+ this .filter .doFilter (this .request , this .response , this .mockFilterChain );
160+ TestObservationRegistryAssert .assertThat (this .observationRegistry )
161+ .hasObservationWithNameEqualTo ("http.server.requests" )
162+ .that ().isNotStopped ();
163+ }
164+
127165 private TestObservationRegistryAssert .TestObservationRegistryAssertReturningObservationContextAssert assertThatHttpObservation () {
166+ TestObservationRegistryAssert .assertThat (this .observationRegistry )
167+ .hasNumberOfObservationsWithNameEqualTo ("http.server.requests" , 1 );
168+
128169 return TestObservationRegistryAssert .assertThat (this .observationRegistry )
129- .hasObservationWithNameEqualTo ("http.server.requests" ).that ();
170+ .hasObservationWithNameEqualTo ("http.server.requests" )
171+ .that ()
172+ .hasBeenStopped ();
173+ }
174+
175+ @ SuppressWarnings ("serial" )
176+ static class ScopeCheckingServlet extends HttpServlet {
177+
178+ private final ObservationRegistry observationRegistry ;
179+
180+ public ScopeCheckingServlet (ObservationRegistry observationRegistry ) {
181+ this .observationRegistry = observationRegistry ;
182+ }
183+
184+ @ Override
185+ protected void doGet (HttpServletRequest req , HttpServletResponse resp ) throws ServletException , IOException {
186+ assertThat (this .observationRegistry .getCurrentObservation ()).isNotNull ();
187+ }
130188 }
131189
132190}
0 commit comments