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 );
@@ -126,9 +141,52 @@ void shouldCloseObservationAfterAsyncCompletion() throws Exception {
126141 assertThatHttpObservation ().hasLowCardinalityKeyValue ("outcome" , "SUCCESS" ).hasBeenStopped ();
127142 }
128143
144+ @ Test
145+ void shouldCloseObservationAfterAsyncError () throws Exception {
146+ this .request .setAsyncSupported (true );
147+ this .request .startAsync ();
148+ this .filter .doFilter (this .request , this .response , this .mockFilterChain );
149+ MockAsyncContext asyncContext = (MockAsyncContext ) this .request .getAsyncContext ();
150+ for (AsyncListener listener : asyncContext .getListeners ()) {
151+ listener .onError (new AsyncEvent (this .request .getAsyncContext (), new IllegalStateException ("test error" )));
152+ }
153+ asyncContext .complete ();
154+ assertThatHttpObservation ().hasLowCardinalityKeyValue ("exception" , "IllegalStateException" ).hasBeenStopped ();
155+ }
156+
157+ @ Test
158+ void shouldNotCloseObservationDuringAsyncDispatch () throws Exception {
159+ this .mockFilterChain = new MockFilterChain (new ScopeCheckingServlet (this .observationRegistry ));
160+ this .request .setDispatcherType (DispatcherType .ASYNC );
161+ this .filter .doFilter (this .request , this .response , this .mockFilterChain );
162+ TestObservationRegistryAssert .assertThat (this .observationRegistry )
163+ .hasObservationWithNameEqualTo ("http.server.requests" )
164+ .that ().isNotStopped ();
165+ }
166+
129167 private TestObservationRegistryAssert .TestObservationRegistryAssertReturningObservationContextAssert assertThatHttpObservation () {
168+ TestObservationRegistryAssert .assertThat (this .observationRegistry )
169+ .hasNumberOfObservationsWithNameEqualTo ("http.server.requests" , 1 );
170+
130171 return TestObservationRegistryAssert .assertThat (this .observationRegistry )
131- .hasObservationWithNameEqualTo ("http.server.requests" ).that ();
172+ .hasObservationWithNameEqualTo ("http.server.requests" )
173+ .that ()
174+ .hasBeenStopped ();
175+ }
176+
177+ @ SuppressWarnings ("serial" )
178+ static class ScopeCheckingServlet extends HttpServlet {
179+
180+ private final ObservationRegistry observationRegistry ;
181+
182+ public ScopeCheckingServlet (ObservationRegistry observationRegistry ) {
183+ this .observationRegistry = observationRegistry ;
184+ }
185+
186+ @ Override
187+ protected void doGet (HttpServletRequest req , HttpServletResponse resp ) throws ServletException , IOException {
188+ assertThat (this .observationRegistry .getCurrentObservation ()).isNotNull ();
189+ }
132190 }
133191
134192}
0 commit comments