@ -16,15 +16,24 @@
package org.springframework.web.filter ;
package org.springframework.web.filter ;
import java.io.IOException ;
import io.micrometer.observation.ObservationRegistry ;
import io.micrometer.observation.ObservationRegistry ;
import io.micrometer.observation.tck.TestObservationRegistry ;
import io.micrometer.observation.tck.TestObservationRegistry ;
import io.micrometer.observation.tck.TestObservationRegistryAssert ;
import io.micrometer.observation.tck.TestObservationRegistryAssert ;
import jakarta.servlet.AsyncEvent ;
import jakarta.servlet.AsyncListener ;
import jakarta.servlet.DispatcherType ;
import jakarta.servlet.RequestDispatcher ;
import jakarta.servlet.RequestDispatcher ;
import jakarta.servlet.ServletException ;
import jakarta.servlet.ServletException ;
import jakarta.servlet.http.HttpServlet ;
import jakarta.servlet.http.HttpServletRequest ;
import jakarta.servlet.http.HttpServletResponse ;
import org.junit.jupiter.api.Test ;
import org.junit.jupiter.api.Test ;
import org.springframework.http.HttpMethod ;
import org.springframework.http.HttpMethod ;
import org.springframework.http.server.observation.ServerRequestObservationContext ;
import org.springframework.http.server.observation.ServerRequestObservationContext ;
import org.springframework.web.testfixture.servlet.MockAsyncContext ;
import org.springframework.web.testfixture.servlet.MockFilterChain ;
import org.springframework.web.testfixture.servlet.MockFilterChain ;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest ;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest ;
import org.springframework.web.testfixture.servlet.MockHttpServletResponse ;
import org.springframework.web.testfixture.servlet.MockHttpServletResponse ;
@ -41,18 +50,18 @@ class ServerHttpObservationFilterTests {
private final TestObservationRegistry observationRegistry = TestObservationRegistry . create ( ) ;
private final TestObservationRegistry observationRegistry = TestObservationRegistry . create ( ) ;
private final ServerHttpObservationFilter filter = new ServerHttpObservationFilter ( this . observationRegistry ) ;
private final MockFilterChain mockFilterChain = new MockFilterChain ( ) ;
private final MockHttpServletRequest request = new MockHttpServletRequest ( HttpMethod . GET . name ( ) , "/resource/test" ) ;
private final MockHttpServletRequest request = new MockHttpServletRequest ( HttpMethod . GET . name ( ) , "/resource/test" ) ;
private final MockHttpServletResponse response = new MockHttpServletResponse ( ) ;
private final MockHttpServletResponse response = new MockHttpServletResponse ( ) ;
private MockFilterChain mockFilterChain = new MockFilterChain ( ) ;
private ServerHttpObservationFilter filter = new ServerHttpObservationFilter ( this . observationRegistry ) ;
@Test
@Test
void filterShouldNotProcessAsyncDispatch ( ) {
void filterShouldProcessAsyncDispatch ( ) {
assertThat ( this . filter . shouldNotFilterAsyncDispatch ( ) ) . isTrue ( ) ;
assertThat ( this . filter . shouldNotFilterAsyncDispatch ( ) ) . isFals e ( ) ;
}
}
@Test
@Test
@ -68,6 +77,12 @@ class ServerHttpObservationFilterTests {
assertThatHttpObservation ( ) . hasLowCardinalityKeyValue ( "outcome" , "SUCCESS" ) . hasBeenStopped ( ) ;
assertThatHttpObservation ( ) . hasLowCardinalityKeyValue ( "outcome" , "SUCCESS" ) . hasBeenStopped ( ) ;
}
}
@Test
void filterShouldOpenScope ( ) throws Exception {
this . mockFilterChain = new MockFilterChain ( new ScopeCheckingServlet ( this . observationRegistry ) ) ;
filter . doFilter ( this . request , this . response , this . mockFilterChain ) ;
}
@Test
@Test
void filterShouldAcceptNoOpObservationContext ( ) throws Exception {
void filterShouldAcceptNoOpObservationContext ( ) throws Exception {
ServerHttpObservationFilter filter = new ServerHttpObservationFilter ( ObservationRegistry . NOOP ) ;
ServerHttpObservationFilter filter = new ServerHttpObservationFilter ( ObservationRegistry . NOOP ) ;
@ -124,9 +139,52 @@ class ServerHttpObservationFilterTests {
assertThatHttpObservation ( ) . hasLowCardinalityKeyValue ( "outcome" , "SUCCESS" ) . hasBeenStopped ( ) ;
assertThatHttpObservation ( ) . hasLowCardinalityKeyValue ( "outcome" , "SUCCESS" ) . hasBeenStopped ( ) ;
}
}
@Test
void shouldCloseObservationAfterAsyncError ( ) throws Exception {
this . request . setAsyncSupported ( true ) ;
this . request . startAsync ( ) ;
this . filter . doFilter ( this . request , this . response , this . mockFilterChain ) ;
MockAsyncContext asyncContext = ( MockAsyncContext ) this . request . getAsyncContext ( ) ;
for ( AsyncListener listener : asyncContext . getListeners ( ) ) {
listener . onError ( new AsyncEvent ( this . request . getAsyncContext ( ) , new IllegalStateException ( "test error" ) ) ) ;
}
asyncContext . complete ( ) ;
assertThatHttpObservation ( ) . hasLowCardinalityKeyValue ( "exception" , "IllegalStateException" ) . hasBeenStopped ( ) ;
}
@Test
void shouldNotCloseObservationDuringAsyncDispatch ( ) throws Exception {
this . mockFilterChain = new MockFilterChain ( new ScopeCheckingServlet ( this . observationRegistry ) ) ;
this . request . setDispatcherType ( DispatcherType . ASYNC ) ;
this . filter . doFilter ( this . request , this . response , this . mockFilterChain ) ;
TestObservationRegistryAssert . assertThat ( this . observationRegistry )
. hasObservationWithNameEqualTo ( "http.server.requests" )
. that ( ) . isNotStopped ( ) ;
}
private TestObservationRegistryAssert . TestObservationRegistryAssertReturningObservationContextAssert assertThatHttpObservation ( ) {
private TestObservationRegistryAssert . TestObservationRegistryAssertReturningObservationContextAssert assertThatHttpObservation ( ) {
TestObservationRegistryAssert . assertThat ( this . observationRegistry )
. hasNumberOfObservationsWithNameEqualTo ( "http.server.requests" , 1 ) ;
return TestObservationRegistryAssert . assertThat ( this . observationRegistry )
return TestObservationRegistryAssert . assertThat ( this . observationRegistry )
. hasObservationWithNameEqualTo ( "http.server.requests" ) . that ( ) ;
. hasObservationWithNameEqualTo ( "http.server.requests" )
. that ( )
. hasBeenStopped ( ) ;
}
@SuppressWarnings ( "serial" )
static class ScopeCheckingServlet extends HttpServlet {
private final ObservationRegistry observationRegistry ;
public ScopeCheckingServlet ( ObservationRegistry observationRegistry ) {
this . observationRegistry = observationRegistry ;
}
@Override
protected void doGet ( HttpServletRequest req , HttpServletResponse resp ) throws ServletException , IOException {
assertThat ( this . observationRegistry . getCurrentObservation ( ) ) . isNotNull ( ) ;
}
}
}
}
}