diff --git a/sentry-servlet-jakarta/api/sentry-servlet-jakarta.api b/sentry-servlet-jakarta/api/sentry-servlet-jakarta.api index a5421e7453f..adde86fda5e 100644 --- a/sentry-servlet-jakarta/api/sentry-servlet-jakarta.api +++ b/sentry-servlet-jakarta/api/sentry-servlet-jakarta.api @@ -9,6 +9,7 @@ public class io/sentry/servlet/jakarta/SentryServletContainerInitializer : jakar } public class io/sentry/servlet/jakarta/SentryServletRequestListener : jakarta/servlet/ServletRequestListener { + public static final field SENTRY_LIFECYCLE_TOKEN_KEY Ljava/lang/String; public fun ()V public fun (Lio/sentry/IScopes;)V public fun requestDestroyed (Ljakarta/servlet/ServletRequestEvent;)V diff --git a/sentry-servlet-jakarta/src/main/java/io/sentry/servlet/jakarta/SentryServletRequestListener.java b/sentry-servlet-jakarta/src/main/java/io/sentry/servlet/jakarta/SentryServletRequestListener.java index 54775386fd7..f5b3be30b97 100644 --- a/sentry-servlet-jakarta/src/main/java/io/sentry/servlet/jakarta/SentryServletRequestListener.java +++ b/sentry-servlet-jakarta/src/main/java/io/sentry/servlet/jakarta/SentryServletRequestListener.java @@ -6,7 +6,9 @@ import io.sentry.Breadcrumb; import io.sentry.Hint; import io.sentry.IScopes; +import io.sentry.ISentryLifecycleToken; import io.sentry.ScopesAdapter; +import io.sentry.util.LifecycleHelper; import io.sentry.util.Objects; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletRequestEvent; @@ -21,6 +23,8 @@ @Open public class SentryServletRequestListener implements ServletRequestListener { + public static final String SENTRY_LIFECYCLE_TOKEN_KEY = "sentry-lifecycle"; + private final IScopes scopes; public SentryServletRequestListener(@NotNull IScopes scopes) { @@ -33,14 +37,16 @@ public SentryServletRequestListener() { @Override public void requestDestroyed(@NotNull ServletRequestEvent servletRequestEvent) { - scopes.popScope(); + final ServletRequest servletRequest = servletRequestEvent.getServletRequest(); + LifecycleHelper.close(servletRequest.getAttribute(SENTRY_LIFECYCLE_TOKEN_KEY)); } @Override public void requestInitialized(@NotNull ServletRequestEvent servletRequestEvent) { - scopes.pushScope(); + final @NotNull ISentryLifecycleToken lifecycleToken = scopes.pushIsolationScope(); final ServletRequest servletRequest = servletRequestEvent.getServletRequest(); + servletRequest.setAttribute(SENTRY_LIFECYCLE_TOKEN_KEY, lifecycleToken); if (servletRequest instanceof HttpServletRequest) { final HttpServletRequest httpRequest = (HttpServletRequest) servletRequest; diff --git a/sentry-servlet-jakarta/src/test/kotlin/io/sentry/servlet/jakarta/SentryServletRequestListenerTest.kt b/sentry-servlet-jakarta/src/test/kotlin/io/sentry/servlet/jakarta/SentryServletRequestListenerTest.kt index 3be76d1cd20..30ef3da1edd 100644 --- a/sentry-servlet-jakarta/src/test/kotlin/io/sentry/servlet/jakarta/SentryServletRequestListenerTest.kt +++ b/sentry-servlet-jakarta/src/test/kotlin/io/sentry/servlet/jakarta/SentryServletRequestListenerTest.kt @@ -2,10 +2,13 @@ package io.sentry.servlet.jakarta import io.sentry.Breadcrumb import io.sentry.IScopes +import io.sentry.ISentryLifecycleToken import jakarta.servlet.ServletRequestEvent import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.check +import org.mockito.kotlin.eq import org.mockito.kotlin.mock +import org.mockito.kotlin.same import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import kotlin.test.Test @@ -14,6 +17,7 @@ import kotlin.test.assertEquals class SentryServletRequestListenerTest { private class Fixture { val scopes = mock() + val lifecycleToken = mock() val listener = SentryServletRequestListener(scopes) val request = mockRequest( @@ -24,6 +28,7 @@ class SentryServletRequestListenerTest { init { whenever(event.servletRequest).thenReturn(request) + whenever(scopes.pushIsolationScope()).thenReturn(lifecycleToken) } } @@ -33,7 +38,7 @@ class SentryServletRequestListenerTest { fun `pushes scope when request gets initialized`() { fixture.listener.requestInitialized(fixture.event) - verify(fixture.scopes).pushScope() + verify(fixture.scopes).pushIsolationScope() } @Test @@ -48,12 +53,14 @@ class SentryServletRequestListenerTest { }, anyOrNull() ) + verify(fixture.request).setAttribute(eq("sentry-lifecycle"), same(fixture.lifecycleToken)) } @Test fun `pops scope when request gets destroyed`() { - fixture.listener.requestDestroyed(fixture.event) + whenever(fixture.request.getAttribute(eq("sentry-lifecycle"))).thenReturn(fixture.lifecycleToken) - verify(fixture.scopes).popScope() + fixture.listener.requestDestroyed(fixture.event) + verify(fixture.lifecycleToken).close() } } diff --git a/sentry-servlet/api/sentry-servlet.api b/sentry-servlet/api/sentry-servlet.api index fd7aee819b5..63d3cf4b331 100644 --- a/sentry-servlet/api/sentry-servlet.api +++ b/sentry-servlet/api/sentry-servlet.api @@ -9,6 +9,7 @@ public class io/sentry/servlet/SentryServletContainerInitializer : javax/servlet } public class io/sentry/servlet/SentryServletRequestListener : javax/servlet/ServletRequestListener { + public static final field SENTRY_LIFECYCLE_TOKEN_KEY Ljava/lang/String; public fun ()V public fun (Lio/sentry/IScopes;)V public fun requestDestroyed (Ljavax/servlet/ServletRequestEvent;)V diff --git a/sentry-servlet/src/main/java/io/sentry/servlet/SentryServletRequestListener.java b/sentry-servlet/src/main/java/io/sentry/servlet/SentryServletRequestListener.java index 97c37e11335..4587daa6551 100644 --- a/sentry-servlet/src/main/java/io/sentry/servlet/SentryServletRequestListener.java +++ b/sentry-servlet/src/main/java/io/sentry/servlet/SentryServletRequestListener.java @@ -6,7 +6,9 @@ import io.sentry.Breadcrumb; import io.sentry.Hint; import io.sentry.IScopes; +import io.sentry.ISentryLifecycleToken; import io.sentry.ScopesAdapter; +import io.sentry.util.LifecycleHelper; import io.sentry.util.Objects; import javax.servlet.ServletRequest; import javax.servlet.ServletRequestEvent; @@ -21,6 +23,8 @@ @Open public class SentryServletRequestListener implements ServletRequestListener { + public static final String SENTRY_LIFECYCLE_TOKEN_KEY = "sentry-lifecycle"; + private final IScopes scopes; public SentryServletRequestListener(@NotNull IScopes scopes) { @@ -33,14 +37,16 @@ public SentryServletRequestListener() { @Override public void requestDestroyed(@NotNull ServletRequestEvent servletRequestEvent) { - scopes.popScope(); + final ServletRequest servletRequest = servletRequestEvent.getServletRequest(); + LifecycleHelper.close(servletRequest.getAttribute(SENTRY_LIFECYCLE_TOKEN_KEY)); } @Override public void requestInitialized(@NotNull ServletRequestEvent servletRequestEvent) { - scopes.pushScope(); + final @NotNull ISentryLifecycleToken lifecycleToken = scopes.pushIsolationScope(); final ServletRequest servletRequest = servletRequestEvent.getServletRequest(); + servletRequest.setAttribute(SENTRY_LIFECYCLE_TOKEN_KEY, lifecycleToken); if (servletRequest instanceof HttpServletRequest) { final HttpServletRequest httpRequest = (HttpServletRequest) servletRequest; diff --git a/sentry-servlet/src/test/kotlin/io/sentry/servlet/SentryServletRequestListenerTest.kt b/sentry-servlet/src/test/kotlin/io/sentry/servlet/SentryServletRequestListenerTest.kt index bfa216f738b..d72b93179fa 100644 --- a/sentry-servlet/src/test/kotlin/io/sentry/servlet/SentryServletRequestListenerTest.kt +++ b/sentry-servlet/src/test/kotlin/io/sentry/servlet/SentryServletRequestListenerTest.kt @@ -2,6 +2,7 @@ package io.sentry.servlet import io.sentry.Breadcrumb import io.sentry.IScopes +import io.sentry.ISentryLifecycleToken import org.assertj.core.api.Assertions.assertThat import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.check @@ -11,10 +12,12 @@ import org.mockito.kotlin.whenever import org.springframework.mock.web.MockHttpServletRequest import javax.servlet.ServletRequestEvent import kotlin.test.Test +import kotlin.test.assertSame class SentryServletRequestListenerTest { private class Fixture { val scopes = mock() + val lifecycleToken = mock() val listener = SentryServletRequestListener(scopes) val request = MockHttpServletRequest() val event = mock() @@ -23,6 +26,7 @@ class SentryServletRequestListenerTest { request.requestURI = "http://localhost:8080/some-uri" request.method = "post" whenever(event.servletRequest).thenReturn(request) + whenever(scopes.pushIsolationScope()).thenReturn(lifecycleToken) } } @@ -32,7 +36,7 @@ class SentryServletRequestListenerTest { fun `pushes scope when request gets initialized`() { fixture.listener.requestInitialized(fixture.event) - verify(fixture.scopes).pushScope() + verify(fixture.scopes).pushIsolationScope() } @Test @@ -47,12 +51,14 @@ class SentryServletRequestListenerTest { }, anyOrNull() ) + assertSame(fixture.lifecycleToken, fixture.request.getAttribute("sentry-lifecycle")) } @Test fun `pops scope when request gets destroyed`() { - fixture.listener.requestDestroyed(fixture.event) + fixture.request.setAttribute("sentry-lifecycle", fixture.lifecycleToken) - verify(fixture.scopes).popScope() + fixture.listener.requestDestroyed(fixture.event) + verify(fixture.lifecycleToken).close() } }