diff --git a/CHANGELOG.md b/CHANGELOG.md index 86d91f82834..6b165b50588 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ - In `TransactionContext.fromPropagationContext` when there is no parent sampling decision, keep the decision `null` so a new sampling decision is made instead of defaulting to `false` - Defer sampling decision by setting `sampled` to `null` in `PropagationContext` when using OpenTelemetry in case of an incoming defer sampling `sentry-trace` header. ([#3945](https://github.com/getsentry/sentry-java/pull/3945)) - Build `PropagationContext` from `SamplingDecision` made by `SentrySampler` instead of parsing headers and potentially ignoring a sampling decision in case a `sentry-trace` header comes in with deferred sampling decision. ([#3947](https://github.com/getsentry/sentry-java/pull/3947)) +- Let OpenTelemetry handle extracting and injecting tracing information ([#3953](https://github.com/getsentry/sentry-java/pull/3953)) + - Our integrations no longer call `.continueTrace` and also do not inject tracing headers if the integration has been added to `ignoredSpanOrigins` ## 8.0.0-rc.1 diff --git a/sentry-apollo-3/src/main/java/io/sentry/apollo3/SentryApollo3HttpInterceptor.kt b/sentry-apollo-3/src/main/java/io/sentry/apollo3/SentryApollo3HttpInterceptor.kt index 52219cb8e1c..abfa41e5e17 100644 --- a/sentry-apollo-3/src/main/java/io/sentry/apollo3/SentryApollo3HttpInterceptor.kt +++ b/sentry-apollo-3/src/main/java/io/sentry/apollo3/SentryApollo3HttpInterceptor.kt @@ -31,6 +31,7 @@ import io.sentry.util.HttpUtils import io.sentry.util.IntegrationUtils.addIntegrationToSdkVersion import io.sentry.util.Platform import io.sentry.util.PropagationTargetsUtils +import io.sentry.util.SpanUtils import io.sentry.util.TracingUtils import io.sentry.util.UrlUtils import io.sentry.vendor.Base64 @@ -120,11 +121,13 @@ class SentryApollo3HttpInterceptor @JvmOverloads constructor( private fun maybeAddTracingHeaders(scopes: IScopes, request: HttpRequest, span: ISpan?): HttpRequest { var cleanedHeaders = removeSentryInternalHeaders(request.headers).toMutableList() - TracingUtils.traceIfAllowed(scopes, request.url, request.headers.filter { it.name == BaggageHeader.BAGGAGE_HEADER }.map { it.value }, span)?.let { - cleanedHeaders.add(HttpHeader(it.sentryTraceHeader.name, it.sentryTraceHeader.value)) - it.baggageHeader?.let { baggageHeader -> - cleanedHeaders = cleanedHeaders.filterNot { it.name == BaggageHeader.BAGGAGE_HEADER }.toMutableList().apply { - add(HttpHeader(baggageHeader.name, baggageHeader.value)) + if (!isIgnored()) { + TracingUtils.traceIfAllowed(scopes, request.url, request.headers.filter { it.name == BaggageHeader.BAGGAGE_HEADER }.map { it.value }, span)?.let { + cleanedHeaders.add(HttpHeader(it.sentryTraceHeader.name, it.sentryTraceHeader.value)) + it.baggageHeader?.let { baggageHeader -> + cleanedHeaders = cleanedHeaders.filterNot { it.name == BaggageHeader.BAGGAGE_HEADER }.toMutableList().apply { + add(HttpHeader(baggageHeader.name, baggageHeader.value)) + } } } } @@ -136,6 +139,10 @@ class SentryApollo3HttpInterceptor @JvmOverloads constructor( return requestBuilder.build() } + private fun isIgnored(): Boolean { + return SpanUtils.isIgnored(scopes.getOptions().getIgnoredSpanOrigins(), TRACE_ORIGIN) + } + private fun removeSentryInternalHeaders(headers: List): List { return headers.filterNot { it.name.equals(SENTRY_APOLLO_3_VARIABLES, true) || diff --git a/sentry-apollo-3/src/test/java/io/sentry/apollo3/SentryApollo3InterceptorTest.kt b/sentry-apollo-3/src/test/java/io/sentry/apollo3/SentryApollo3InterceptorTest.kt index e5cb93eb3da..30689f0b3b5 100644 --- a/sentry-apollo-3/src/test/java/io/sentry/apollo3/SentryApollo3InterceptorTest.kt +++ b/sentry-apollo-3/src/test/java/io/sentry/apollo3/SentryApollo3InterceptorTest.kt @@ -208,6 +208,16 @@ class SentryApollo3InterceptorTest { assertNotNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) } + @Test + fun `does not add sentry-trace header when span origin is ignored`() { + fixture.options.ignoredSpanOrigins = listOf("auto.graphql.apollo3") + executeQuery(isSpanActive = false) + + val recorderRequest = fixture.server.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! + assertNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) + assertNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) + } + @Test fun `when there is an active span, adds sentry trace headers to the request`() { executeQuery() diff --git a/sentry-apollo/src/main/java/io/sentry/apollo/SentryApolloInterceptor.kt b/sentry-apollo/src/main/java/io/sentry/apollo/SentryApolloInterceptor.kt index dd5fdd39801..a24507ce513 100644 --- a/sentry-apollo/src/main/java/io/sentry/apollo/SentryApolloInterceptor.kt +++ b/sentry-apollo/src/main/java/io/sentry/apollo/SentryApolloInterceptor.kt @@ -25,6 +25,7 @@ import io.sentry.SpanStatus import io.sentry.TypeCheckHint.APOLLO_REQUEST import io.sentry.TypeCheckHint.APOLLO_RESPONSE import io.sentry.util.IntegrationUtils.addIntegrationToSdkVersion +import io.sentry.util.SpanUtils import io.sentry.util.TracingUtils import java.util.Locale import java.util.concurrent.Executor @@ -115,7 +116,7 @@ class SentryApolloInterceptor( private fun addTracingHeaders(request: InterceptorRequest, span: ISpan?): RequestHeaders { val requestHeaderBuilder = request.requestHeaders.toBuilder() - if (scopes.options.isTraceSampling) { + if (scopes.options.isTraceSampling && !isIgnored()) { // we have no access to URI, no way to verify tracing origins TracingUtils.trace( scopes, @@ -135,6 +136,10 @@ class SentryApolloInterceptor( return requestHeaderBuilder.build() } + private fun isIgnored(): Boolean { + return SpanUtils.isIgnored(scopes.getOptions().getIgnoredSpanOrigins(), TRACE_ORIGIN) + } + private fun startChild(request: InterceptorRequest, activeSpan: ISpan): ISpan { val operation = request.operation.name().name() val operationType = when (request.operation) { diff --git a/sentry-apollo/src/test/java/io/sentry/apollo/SentryApolloInterceptorTest.kt b/sentry-apollo/src/test/java/io/sentry/apollo/SentryApolloInterceptorTest.kt index 1c56af13bd1..97d13555f0a 100644 --- a/sentry-apollo/src/test/java/io/sentry/apollo/SentryApolloInterceptorTest.kt +++ b/sentry-apollo/src/test/java/io/sentry/apollo/SentryApolloInterceptorTest.kt @@ -161,6 +161,16 @@ class SentryApolloInterceptorTest { assertNotNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) } + @Test + fun `does not add sentry-trace header when span origin is ignored`() { + fixture.options.ignoredSpanOrigins = listOf("auto.graphql.apollo") + executeQuery(isSpanActive = false) + + val recorderRequest = fixture.server.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! + assertNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) + assertNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) + } + @Test fun `when there is an active span, adds sentry trace headers to the request`() { executeQuery() diff --git a/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpInterceptor.kt b/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpInterceptor.kt index ade89df4941..370b3ccb6bc 100644 --- a/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpInterceptor.kt +++ b/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpInterceptor.kt @@ -18,6 +18,7 @@ import io.sentry.transport.CurrentDateProvider import io.sentry.util.IntegrationUtils.addIntegrationToSdkVersion import io.sentry.util.Platform import io.sentry.util.PropagationTargetsUtils +import io.sentry.util.SpanUtils import io.sentry.util.TracingUtils import io.sentry.util.UrlUtils import okhttp3.Interceptor @@ -93,16 +94,21 @@ public open class SentryOkHttpInterceptor( try { val requestBuilder = request.newBuilder() - TracingUtils.traceIfAllowed( - scopes, - request.url.toString(), - request.headers(BaggageHeader.BAGGAGE_HEADER), - span - )?.let { tracingHeaders -> - requestBuilder.addHeader(tracingHeaders.sentryTraceHeader.name, tracingHeaders.sentryTraceHeader.value) - tracingHeaders.baggageHeader?.let { - requestBuilder.removeHeader(BaggageHeader.BAGGAGE_HEADER) - requestBuilder.addHeader(it.name, it.value) + if (!isIgnored()) { + TracingUtils.traceIfAllowed( + scopes, + request.url.toString(), + request.headers(BaggageHeader.BAGGAGE_HEADER), + span + )?.let { tracingHeaders -> + requestBuilder.addHeader( + tracingHeaders.sentryTraceHeader.name, + tracingHeaders.sentryTraceHeader.value + ) + tracingHeaders.baggageHeader?.let { + requestBuilder.removeHeader(BaggageHeader.BAGGAGE_HEADER) + requestBuilder.addHeader(it.name, it.value) + } } } @@ -144,6 +150,10 @@ public open class SentryOkHttpInterceptor( } } + private fun isIgnored(): Boolean { + return SpanUtils.isIgnored(scopes.getOptions().getIgnoredSpanOrigins(), TRACE_ORIGIN) + } + private fun sendBreadcrumb( request: Request, code: Int?, diff --git a/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpInterceptorTest.kt b/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpInterceptorTest.kt index 4bda92f3c48..53b4cca93a9 100644 --- a/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpInterceptorTest.kt +++ b/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpInterceptorTest.kt @@ -10,6 +10,7 @@ import io.sentry.IScope import io.sentry.IScopes import io.sentry.Scope import io.sentry.ScopeCallback +import io.sentry.Sentry import io.sentry.SentryOptions import io.sentry.SentryTraceHeader import io.sentry.SentryTracer @@ -73,16 +74,18 @@ class SentryOkHttpInterceptorTest { HttpStatusCodeRange.DEFAULT_MAX ) ), - sendDefaultPii: Boolean = false + sendDefaultPii: Boolean = false, + optionsConfiguration: Sentry.OptionsConfiguration? = null ): OkHttpClient { - options = SentryOptions().apply { - dsn = "https://key@sentry.io/proj" + options = SentryOptions().also { + optionsConfiguration?.configure(it) + it.dsn = "https://key@sentry.io/proj" if (includeMockServerInTracePropagationTargets) { - setTracePropagationTargets(listOf(server.hostName)) + it.setTracePropagationTargets(listOf(server.hostName)) } else if (!keepDefaultTracePropagationTargets) { - setTracePropagationTargets(listOf("other-api")) + it.setTracePropagationTargets(listOf("other-api")) } - isSendDefaultPii = sendDefaultPii + it.isSendDefaultPii = sendDefaultPii } scope = Scope(options) whenever(scopes.options).thenReturn(options) @@ -207,6 +210,17 @@ class SentryOkHttpInterceptorTest { assertNotNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) } + @Test + fun `does not add sentry-trace header when span origin is ignored`() { + val sut = fixture.getSut(isSpanActive = false) { options -> + options.ignoredSpanOrigins = listOf("auto.http.okhttp") + } + sut.newCall(getRequest()).execute() + val recorderRequest = fixture.server.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! + assertNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) + assertNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) + } + @Test fun `when there is no active span and host if not allowed, does not add sentry trace header to the request`() { val sut = fixture.getSut(isSpanActive = false) diff --git a/sentry-openfeign/src/main/java/io/sentry/openfeign/SentryFeignClient.java b/sentry-openfeign/src/main/java/io/sentry/openfeign/SentryFeignClient.java index a57380b29a3..935c4229ab1 100644 --- a/sentry-openfeign/src/main/java/io/sentry/openfeign/SentryFeignClient.java +++ b/sentry-openfeign/src/main/java/io/sentry/openfeign/SentryFeignClient.java @@ -15,6 +15,7 @@ import io.sentry.SpanOptions; import io.sentry.SpanStatus; import io.sentry.util.Objects; +import io.sentry.util.SpanUtils; import io.sentry.util.TracingUtils; import io.sentry.util.UrlUtils; import java.io.IOException; @@ -98,6 +99,10 @@ public Response execute(final @NotNull Request request, final @NotNull Request.O private @NotNull Request maybeAddTracingHeaders( final @NotNull Request request, final @Nullable ISpan span) { + if (isIgnored()) { + return request; + } + final @NotNull RequestWrapper requestWrapper = new RequestWrapper(request); final @Nullable Collection requestBaggageHeaders = request.headers().get(BaggageHeader.BAGGAGE_HEADER); @@ -124,6 +129,10 @@ public Response execute(final @NotNull Request request, final @NotNull Request.O return requestWrapper.build(); } + private boolean isIgnored() { + return SpanUtils.isIgnored(scopes.getOptions().getIgnoredSpanOrigins(), TRACE_ORIGIN); + } + private void addBreadcrumb(final @NotNull Request request, final @Nullable Response response) { final Breadcrumb breadcrumb = Breadcrumb.http( diff --git a/sentry-openfeign/src/test/kotlin/io/sentry/openfeign/SentryFeignClientTest.kt b/sentry-openfeign/src/test/kotlin/io/sentry/openfeign/SentryFeignClientTest.kt index 28d65da4f18..e34feed7ff9 100644 --- a/sentry-openfeign/src/test/kotlin/io/sentry/openfeign/SentryFeignClientTest.kt +++ b/sentry-openfeign/src/test/kotlin/io/sentry/openfeign/SentryFeignClientTest.kt @@ -130,6 +130,17 @@ class SentryFeignClientTest { assertNotNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) } + @Test + fun `does not add sentry trace header when span origin is ignored`() { + fixture.sentryOptions.dsn = "https://key@sentry.io/proj" + fixture.sentryOptions.ignoredSpanOrigins = listOf("auto.http.openfeign") + val sut = fixture.getSut(isSpanActive = false) + sut.getOk() + val recorderRequest = fixture.server.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! + assertNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) + assertNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) + } + @Test fun `when there is no active span, does not add sentry trace header to the request if host is disallowed`() { fixture.sentryOptions.setTracePropagationTargets(listOf("some-host-that-does-not-exist")) diff --git a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanRestClientCustomizerTest.kt b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanRestClientCustomizerTest.kt index d32885b41fa..2f98188eef2 100644 --- a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanRestClientCustomizerTest.kt +++ b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanRestClientCustomizerTest.kt @@ -17,6 +17,7 @@ import okhttp3.mockwebserver.MockWebServer import okhttp3.mockwebserver.SocketPolicy import org.apache.hc.client5.http.impl.classic.HttpClients import org.assertj.core.api.Assertions.assertThat +import org.junit.Assert.assertNull import org.mockito.Mockito.doAnswer import org.mockito.Mockito.mock import org.mockito.Mockito.verify @@ -248,6 +249,24 @@ class SentrySpanRestClientCustomizerTest { assertTrue(baggageHeaderValues[0].contains("sentry-trace_id")) } + @Test + fun `does not add sentry-trace header if span origin is ignored`() { + fixture.sentryOptions.ignoredSpanOrigins = listOf("auto.http.spring_jakarta.restclient") + val sut = fixture.getSut(isTransactionActive = false) + val headers = HttpHeaders() + + sut.build() + .get() + .uri(fixture.url) + .httpRequest { it.headers.addAll(headers) } + .retrieve() + .toEntity(String::class.java) + + val recorderRequest = fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! + assertNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) + assertNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) + } + @Test fun `when transaction is active adds breadcrumb when http calls succeeds`() { fixture.getSut(isTransactionActive = true) diff --git a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanRestTemplateCustomizerTest.kt b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanRestTemplateCustomizerTest.kt index f6fe3b69bc9..b928fd62892 100644 --- a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanRestTemplateCustomizerTest.kt +++ b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanRestTemplateCustomizerTest.kt @@ -16,6 +16,7 @@ import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.MockWebServer import okhttp3.mockwebserver.SocketPolicy import org.assertj.core.api.Assertions.assertThat +import org.junit.Assert.assertNull import org.mockito.kotlin.any import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.check @@ -198,6 +199,20 @@ class SentrySpanRestTemplateCustomizerTest { assertTrue(baggageHeaderValues[0].contains("sentry-trace_id")) } + @Test + fun `does not add sentry-trace header when span origin is ignored`() { + fixture.sentryOptions.ignoredSpanOrigins = listOf("auto.http.spring_jakarta.resttemplate") + val sut = fixture.getSut(isTransactionActive = false) + val headers = HttpHeaders() + val requestEntity = HttpEntity(headers) + + sut.exchange(fixture.url, HttpMethod.GET, requestEntity, String::class.java) + + val recorderRequest = fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! + assertNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) + assertNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) + } + @Test fun `avoids duplicate registration`() { val restTemplate = fixture.getSut(isTransactionActive = true) diff --git a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanWebClientCustomizerTest.kt b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanWebClientCustomizerTest.kt index decbb68e9ee..55f43c8121e 100644 --- a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanWebClientCustomizerTest.kt +++ b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanWebClientCustomizerTest.kt @@ -6,6 +6,7 @@ import io.sentry.IScope import io.sentry.IScopes import io.sentry.Scope import io.sentry.ScopeCallback +import io.sentry.Sentry.OptionsConfiguration import io.sentry.SentryOptions import io.sentry.SentryTraceHeader import io.sentry.SentryTracer @@ -46,14 +47,15 @@ class SentrySpanWebClientCustomizerTest { lateinit var transaction: SentryTracer private val customizer = SentrySpanWebClientCustomizer(scopes) - fun getSut(isTransactionActive: Boolean, status: HttpStatus = HttpStatus.OK, throwIOException: Boolean = false, includeMockServerInTracingOrigins: Boolean = true): WebClient { - sentryOptions = SentryOptions().apply { + fun getSut(isTransactionActive: Boolean, status: HttpStatus = HttpStatus.OK, throwIOException: Boolean = false, includeMockServerInTracingOrigins: Boolean = true, optionsConfiguration: OptionsConfiguration? = null): WebClient { + sentryOptions = SentryOptions().also { + optionsConfiguration?.configure(it) if (includeMockServerInTracingOrigins) { - setTracePropagationTargets(listOf(mockServer.hostName)) + it.setTracePropagationTargets(listOf(mockServer.hostName)) } else { - setTracePropagationTargets(listOf("other-api")) + it.setTracePropagationTargets(listOf("other-api")) } - dsn = "http://key@localhost/proj" + it.dsn = "http://key@localhost/proj" } scope = Scope(sentryOptions) whenever(scopes.options).thenReturn(sentryOptions) @@ -163,6 +165,22 @@ class SentrySpanWebClientCustomizerTest { assertNotNull(recordedRequest.headers[BaggageHeader.BAGGAGE_HEADER]) } + @Test + fun `does not add sentry-trace header when span origin is ignored`() { + val sut = fixture.getSut(isTransactionActive = false, includeMockServerInTracingOrigins = true) { options -> + options.ignoredSpanOrigins = listOf("auto.http.spring_jakarta.webclient") + } + sut + .get() + .uri(fixture.mockServer.url("/test/123").toUri()) + .retrieve() + .bodyToMono(String::class.java) + .block() + val recordedRequest = fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! + assertNull(recordedRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) + assertNull(recordedRequest.headers[BaggageHeader.BAGGAGE_HEADER]) + } + @Test fun `when transaction is active and server is listed in tracing origins, adds sentry trace header to the request`() { fixture.getSut(isTransactionActive = true) diff --git a/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentrySpanRestTemplateCustomizerTest.kt b/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentrySpanRestTemplateCustomizerTest.kt index 5d09db87d56..e20ebed3fb9 100644 --- a/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentrySpanRestTemplateCustomizerTest.kt +++ b/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentrySpanRestTemplateCustomizerTest.kt @@ -16,6 +16,7 @@ import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.MockWebServer import okhttp3.mockwebserver.SocketPolicy import org.assertj.core.api.Assertions.assertThat +import org.junit.Assert.assertNull import org.mockito.kotlin.any import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.check @@ -200,6 +201,20 @@ class SentrySpanRestTemplateCustomizerTest { assertTrue(baggageHeaderValues[0].contains("sentry-trace_id")) } + @Test + fun `does not add sentry-trace header when span origin is ignored`() { + fixture.sentryOptions.ignoredSpanOrigins = listOf("auto.http.spring.resttemplate") + val sut = fixture.getSut(isTransactionActive = false) + val headers = HttpHeaders() + val requestEntity = HttpEntity(headers) + + sut.exchange(fixture.url, HttpMethod.GET, requestEntity, String::class.java) + + val recorderRequest = fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! + assertNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) + assertNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) + } + @Test fun `avoids duplicate registration`() { val restTemplate = fixture.getSut(isTransactionActive = true) diff --git a/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentrySpanWebClientCustomizerTest.kt b/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentrySpanWebClientCustomizerTest.kt index ac63805436b..f72232da2f1 100644 --- a/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentrySpanWebClientCustomizerTest.kt +++ b/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentrySpanWebClientCustomizerTest.kt @@ -6,6 +6,7 @@ import io.sentry.IScope import io.sentry.IScopes import io.sentry.Scope import io.sentry.ScopeCallback +import io.sentry.Sentry.OptionsConfiguration import io.sentry.SentryOptions import io.sentry.SentryTraceHeader import io.sentry.SentryTracer @@ -46,14 +47,15 @@ class SentrySpanWebClientCustomizerTest { lateinit var transaction: SentryTracer private val customizer = SentrySpanWebClientCustomizer(scopes) - fun getSut(isTransactionActive: Boolean, status: HttpStatus = HttpStatus.OK, throwIOException: Boolean = false, includeMockServerInTracingOrigins: Boolean = true): WebClient { - sentryOptions = SentryOptions().apply { + fun getSut(isTransactionActive: Boolean, status: HttpStatus = HttpStatus.OK, throwIOException: Boolean = false, includeMockServerInTracingOrigins: Boolean = true, optionsConfiguration: OptionsConfiguration? = null): WebClient { + sentryOptions = SentryOptions().also { + optionsConfiguration?.configure(it) if (includeMockServerInTracingOrigins) { - setTracePropagationTargets(listOf(mockServer.hostName)) + it.setTracePropagationTargets(listOf(mockServer.hostName)) } else { - setTracePropagationTargets(listOf("other-api")) + it.setTracePropagationTargets(listOf("other-api")) } - dsn = "http://key@localhost/proj" + it.dsn = "http://key@localhost/proj" } scope = Scope(sentryOptions) whenever(scopes.options).thenReturn(sentryOptions) @@ -165,6 +167,22 @@ class SentrySpanWebClientCustomizerTest { assertNotNull(recordedRequest.headers[BaggageHeader.BAGGAGE_HEADER]) } + @Test + fun `does not add sentry-trace header when span origin is ignored`() { + val sut = fixture.getSut(isTransactionActive = false, includeMockServerInTracingOrigins = true) { options -> + options.ignoredSpanOrigins = listOf("auto.http.spring.webclient") + } + sut + .get() + .uri(fixture.mockServer.url("/test/123").toUri()) + .retrieve() + .bodyToMono(String::class.java) + .block() + val recordedRequest = fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! + assertNull(recordedRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) + assertNull(recordedRequest.headers[BaggageHeader.BAGGAGE_HEADER]) + } + @Test fun `when transaction is active and server is listed in tracing origins, adds sentry trace header to the request`() { fixture.getSut(isTransactionActive = true) diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientHttpRequestInterceptor.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientHttpRequestInterceptor.java index 6feac7eec7d..f50c93976e5 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientHttpRequestInterceptor.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientHttpRequestInterceptor.java @@ -14,6 +14,7 @@ import io.sentry.SpanOptions; import io.sentry.SpanStatus; import io.sentry.util.Objects; +import io.sentry.util.SpanUtils; import io.sentry.util.TracingUtils; import io.sentry.util.UrlUtils; import java.io.IOException; @@ -90,6 +91,10 @@ public SentrySpanClientHttpRequestInterceptor( private void maybeAddTracingHeaders( final @NotNull HttpRequest request, final @Nullable ISpan span) { + if (isIgnored()) { + return; + } + final @Nullable TracingUtils.TracingHeaders tracingHeaders = TracingUtils.traceIfAllowed( scopes, @@ -111,6 +116,10 @@ private void maybeAddTracingHeaders( } } + private boolean isIgnored() { + return SpanUtils.isIgnored(scopes.getOptions().getIgnoredSpanOrigins(), traceOrigin); + } + private void addBreadcrumb( final @NotNull HttpRequest request, final @NotNull byte[] body, diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientWebRequestFilter.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientWebRequestFilter.java index 6744cf174eb..1189532c0c4 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientWebRequestFilter.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientWebRequestFilter.java @@ -13,6 +13,7 @@ import io.sentry.SpanOptions; import io.sentry.SpanStatus; import io.sentry.util.Objects; +import io.sentry.util.SpanUtils; import io.sentry.util.TracingUtils; import java.util.Locale; import org.jetbrains.annotations.NotNull; @@ -71,6 +72,10 @@ public SentrySpanClientWebRequestFilter(final @NotNull IScopes scopes) { private @NotNull ClientRequest maybeAddTracingHeaders( final @NotNull ClientRequest request, final @Nullable ISpan span) { + if (isIgnored()) { + return request; + } + final ClientRequest.Builder requestBuilder = ClientRequest.from(request); final @Nullable TracingUtils.TracingHeaders tracingHeaders = @@ -98,6 +103,10 @@ public SentrySpanClientWebRequestFilter(final @NotNull IScopes scopes) { return requestBuilder.build(); } + private boolean isIgnored() { + return SpanUtils.isIgnored(scopes.getOptions().getIgnoredSpanOrigins(), TRACE_ORIGIN); + } + private void addBreadcrumb( final @NotNull ClientRequest request, final @Nullable ClientResponse response) { final Breadcrumb breadcrumb = diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTracingFilter.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTracingFilter.java index fdfef1030c4..bd1ffbab812 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTracingFilter.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTracingFilter.java @@ -12,6 +12,7 @@ import io.sentry.TransactionOptions; import io.sentry.protocol.TransactionNameSource; import io.sentry.util.Objects; +import io.sentry.util.SpanUtils; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; @@ -77,7 +78,7 @@ protected void doFilterInternal( final @NotNull HttpServletResponse httpResponse, final @NotNull FilterChain filterChain) throws ServletException, IOException { - if (scopes.isEnabled()) { + if (scopes.isEnabled() && !isIgnored()) { final @Nullable String sentryTraceHeader = httpRequest.getHeader(SentryTraceHeader.SENTRY_TRACE_HEADER); final @Nullable List baggageHeader = @@ -94,6 +95,10 @@ protected void doFilterInternal( } } + private boolean isIgnored() { + return SpanUtils.isIgnored(scopes.getOptions().getIgnoredSpanOrigins(), TRACE_ORIGIN); + } + private void doFilterWithTransaction( HttpServletRequest httpRequest, HttpServletResponse httpResponse, diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter.java index 2d43b41f852..57b7b86e40f 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter.java @@ -18,6 +18,7 @@ import io.sentry.TransactionOptions; import io.sentry.protocol.TransactionNameSource; import io.sentry.util.Objects; +import io.sentry.util.SpanUtils; import java.util.List; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -52,7 +53,7 @@ public AbstractSentryWebFilter(final @NotNull IScopes scopes) { final @NotNull IScopes requestScopes, final @NotNull ServerHttpRequest request, final @NotNull String origin) { - if (requestScopes.isEnabled()) { + if (requestScopes.isEnabled() && !isIgnored(requestScopes, origin)) { final @NotNull HttpHeaders headers = request.getHeaders(); final @Nullable String sentryTraceHeader = headers.getFirst(SentryTraceHeader.SENTRY_TRACE_HEADER); @@ -69,6 +70,10 @@ && shouldTraceRequest(requestScopes, request)) { return null; } + private boolean isIgnored(final @NotNull IScopes scopes, final @NotNull String origin) { + return SpanUtils.isIgnored(scopes.getOptions().getIgnoredSpanOrigins(), origin); + } + protected void doFinally( final @NotNull ServerWebExchange serverWebExchange, final @NotNull IScopes requestScopes, diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTracingFilterTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTracingFilterTest.kt index 69eda6747b6..864c5a57115 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTracingFilterTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTracingFilterTest.kt @@ -218,7 +218,7 @@ class SentryTracingFilterTest { verify(fixture.scopes).isEnabled verify(fixture.scopes).continueTrace(anyOrNull(), anyOrNull()) - verify(fixture.scopes, times(2)).options + verify(fixture.scopes, times(3)).options verifyNoMoreInteractions(fixture.scopes) verify(fixture.transactionNameProvider, never()).provideTransactionName(any()) } @@ -284,4 +284,27 @@ class SentryTracingFilterTest { anyOrNull() ) } + + @Test + fun `does not continue incoming trace if span origin is ignored`() { + val parentSpanId = SpanId() + val sentryTraceHeaderString = "2722d9f6ec019ade60c776169d9a8904-$parentSpanId-1" + val baggageHeaderStrings = listOf("sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-trace_id=2722d9f6ec019ade60c776169d9a8904,sentry-transaction=HTTP%20GET") + fixture.options.tracesSampleRate = null + fixture.options.ignoredSpanOrigins = listOf("auto.http.spring_jakarta.webmvc") + val filter = fixture.getSut(sentryTraceHeader = sentryTraceHeaderString, baggageHeaders = baggageHeaderStrings) + + filter.doFilter(fixture.request, fixture.response, fixture.chain) + + verify(fixture.chain).doFilter(fixture.request, fixture.response) + + verify(fixture.scopes, never()).continueTrace(any(), any()) + + verify(fixture.scopes, never()).captureTransaction( + anyOrNull(), + anyOrNull(), + anyOrNull(), + anyOrNull() + ) + } } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebFluxTracingFilterTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebFluxTracingFilterTest.kt index c99389bc426..bfd2d9e1adb 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebFluxTracingFilterTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebFluxTracingFilterTest.kt @@ -248,7 +248,7 @@ class SentryWebFluxTracingFilterTest { verify(fixture.chain).filter(fixture.exchange) verify(fixture.scopes, times(2)).isEnabled - verify(fixture.scopes, times(2)).options + verify(fixture.scopes, times(3)).options verify(fixture.scopes).continueTrace(anyOrNull(), anyOrNull()) verify(fixture.scopes).addBreadcrumb(any(), any()) verify(fixture.scopes).configureScope(any()) @@ -323,4 +323,29 @@ class SentryWebFluxTracingFilterTest { verify(fixture.scopes).continueTrace(eq(sentryTraceHeaderString), eq(baggageHeaderStrings)) } } + + @Test + fun `does not continue incoming trace is span origin is ignored`() { + val parentSpanId = SpanId() + val sentryTraceHeaderString = "2722d9f6ec019ade60c776169d9a8904-$parentSpanId-1" + val baggageHeaderStrings = listOf("sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-trace_id=2722d9f6ec019ade60c776169d9a8904,sentry-transaction=HTTP%20GET") + fixture.options.tracesSampleRate = null + fixture.options.ignoredSpanOrigins = listOf("auto.spring_jakarta.webflux") + val filter = fixture.getSut(sentryTraceHeader = sentryTraceHeaderString, baggageHeaders = baggageHeaderStrings) + + withMockScopes { + filter.filter(fixture.exchange, fixture.chain).block() + + verify(fixture.chain).filter(fixture.exchange) + + verify(fixture.scopes, never()).captureTransaction( + anyOrNull(), + anyOrNull(), + anyOrNull(), + anyOrNull() + ) + + verify(fixture.scopes, never()).continueTrace(any(), any()) + } + } } diff --git a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientHttpRequestInterceptor.java b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientHttpRequestInterceptor.java index 91ca625d418..17be912a2c6 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientHttpRequestInterceptor.java +++ b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientHttpRequestInterceptor.java @@ -14,6 +14,7 @@ import io.sentry.SpanOptions; import io.sentry.SpanStatus; import io.sentry.util.Objects; +import io.sentry.util.SpanUtils; import io.sentry.util.TracingUtils; import io.sentry.util.UrlUtils; import java.io.IOException; @@ -82,6 +83,10 @@ public SentrySpanClientHttpRequestInterceptor(final @NotNull IScopes scopes) { private void maybeAddTracingHeaders( final @NotNull HttpRequest request, final @Nullable ISpan span) { + if (isIgnored()) { + return; + } + final @Nullable TracingUtils.TracingHeaders tracingHeaders = TracingUtils.traceIfAllowed( scopes, @@ -103,6 +108,10 @@ private void maybeAddTracingHeaders( } } + private boolean isIgnored() { + return SpanUtils.isIgnored(scopes.getOptions().getIgnoredSpanOrigins(), TRACE_ORIGIN); + } + private void addBreadcrumb( final @NotNull HttpRequest request, final @NotNull byte[] body, diff --git a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientWebRequestFilter.java b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientWebRequestFilter.java index d401041544a..e9d787a3dec 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientWebRequestFilter.java +++ b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientWebRequestFilter.java @@ -13,6 +13,7 @@ import io.sentry.SpanOptions; import io.sentry.SpanStatus; import io.sentry.util.Objects; +import io.sentry.util.SpanUtils; import io.sentry.util.TracingUtils; import io.sentry.util.UrlUtils; import java.util.Locale; @@ -73,6 +74,10 @@ public SentrySpanClientWebRequestFilter(final @NotNull IScopes scopes) { private ClientRequest maybeAddHeaders( final @NotNull ClientRequest request, final @Nullable ISpan span) { + if (isIgnored()) { + return request; + } + final ClientRequest.Builder requestBuilder = ClientRequest.from(request); final @Nullable TracingUtils.TracingHeaders tracingHeaders = @@ -100,6 +105,10 @@ private ClientRequest maybeAddHeaders( return requestBuilder.build(); } + private boolean isIgnored() { + return SpanUtils.isIgnored(scopes.getOptions().getIgnoredSpanOrigins(), TRACE_ORIGIN); + } + private void addBreadcrumb( final @NotNull ClientRequest request, final @Nullable ClientResponse response) { final Breadcrumb breadcrumb = diff --git a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTracingFilter.java b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTracingFilter.java index 1e5cffc5687..8f228f80b72 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTracingFilter.java +++ b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTracingFilter.java @@ -12,6 +12,7 @@ import io.sentry.TransactionOptions; import io.sentry.protocol.TransactionNameSource; import io.sentry.util.Objects; +import io.sentry.util.SpanUtils; import java.io.IOException; import java.util.Collections; import java.util.List; @@ -75,7 +76,7 @@ protected void doFilterInternal( final @NotNull FilterChain filterChain) throws ServletException, IOException { - if (scopes.isEnabled()) { + if (scopes.isEnabled() && !isIgnored()) { final @Nullable String sentryTraceHeader = httpRequest.getHeader(SentryTraceHeader.SENTRY_TRACE_HEADER); final @Nullable List baggageHeader = @@ -93,6 +94,10 @@ protected void doFilterInternal( } } + private boolean isIgnored() { + return SpanUtils.isIgnored(scopes.getOptions().getIgnoredSpanOrigins(), TRACE_ORIGIN); + } + private void doFilterWithTransaction( HttpServletRequest httpRequest, HttpServletResponse httpResponse, diff --git a/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java b/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java index ee5a5a7094b..118db63db98 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java +++ b/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java @@ -18,6 +18,7 @@ import io.sentry.TransactionOptions; import io.sentry.protocol.TransactionNameSource; import io.sentry.util.Objects; +import io.sentry.util.SpanUtils; import java.util.List; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -55,7 +56,7 @@ public Mono filter( final @NotNull ServerWebExchange serverWebExchange, final @NotNull WebFilterChain webFilterChain) { @NotNull IScopes requestScopes = Sentry.forkedRootScopes("request.webflux"); - if (!requestScopes.isEnabled()) { + if (!requestScopes.isEnabled() || isIgnored(requestScopes)) { return webFilterChain.filter(serverWebExchange); } @@ -107,6 +108,10 @@ isTracingEnabled && shouldTraceRequest(requestScopes, request) }); } + private boolean isIgnored(final @NotNull IScopes scopes) { + return SpanUtils.isIgnored(scopes.getOptions().getIgnoredSpanOrigins(), TRACE_ORIGIN); + } + private boolean shouldTraceRequest( final @NotNull IScopes scopes, final @NotNull ServerHttpRequest request) { return scopes.getOptions().isTraceOptionsRequests() diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTracingFilterTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTracingFilterTest.kt index 4285d3bd614..c6eac05a146 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTracingFilterTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTracingFilterTest.kt @@ -217,7 +217,7 @@ class SentryTracingFilterTest { verify(fixture.chain).doFilter(fixture.request, fixture.response) verify(fixture.scopes).isEnabled - verify(fixture.scopes, times(2)).options + verify(fixture.scopes, times(3)).options verify(fixture.scopes).continueTrace(anyOrNull(), anyOrNull()) verifyNoMoreInteractions(fixture.scopes) verify(fixture.transactionNameProvider, never()).provideTransactionName(any()) @@ -284,4 +284,27 @@ class SentryTracingFilterTest { anyOrNull() ) } + + @Test + fun `does not continue incoming trace if span origin is ignored`() { + val parentSpanId = SpanId() + val sentryTraceHeaderString = "2722d9f6ec019ade60c776169d9a8904-$parentSpanId-1" + val baggageHeaderStrings = listOf("sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-trace_id=2722d9f6ec019ade60c776169d9a8904,sentry-transaction=HTTP%20GET") + fixture.options.tracesSampleRate = null + fixture.options.ignoredSpanOrigins = listOf("auto.http.spring.webmvc") + val filter = fixture.getSut(sentryTraceHeader = sentryTraceHeaderString, baggageHeaders = baggageHeaderStrings) + + filter.doFilter(fixture.request, fixture.response, fixture.chain) + + verify(fixture.chain).doFilter(fixture.request, fixture.response) + + verify(fixture.scopes, never()).continueTrace(any(), any()) + + verify(fixture.scopes, never()).captureTransaction( + anyOrNull(), + anyOrNull(), + anyOrNull(), + anyOrNull() + ) + } } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebFluxTracingFilterTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebFluxTracingFilterTest.kt index 8316762cc5d..56e925ab3fd 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebFluxTracingFilterTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebFluxTracingFilterTest.kt @@ -249,7 +249,7 @@ class SentryWebFluxTracingFilterTest { verify(fixture.chain).filter(fixture.exchange) verify(fixture.scopes).isEnabled - verify(fixture.scopes, times(2)).options + verify(fixture.scopes, times(3)).options verify(fixture.scopes).continueTrace(anyOrNull(), anyOrNull()) verify(fixture.scopes).addBreadcrumb(any(), any()) verify(fixture.scopes).configureScope(any()) @@ -324,4 +324,29 @@ class SentryWebFluxTracingFilterTest { verify(fixture.scopes).continueTrace(eq(sentryTraceHeaderString), eq(baggageHeaderStrings)) } } + + @Test + fun `does not continue incoming trace if span origin is ignored`() { + val parentSpanId = SpanId() + val sentryTraceHeaderString = "2722d9f6ec019ade60c776169d9a8904-$parentSpanId-1" + val baggageHeaderStrings = listOf("sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-trace_id=2722d9f6ec019ade60c776169d9a8904,sentry-transaction=HTTP%20GET") + fixture.options.tracesSampleRate = null + fixture.options.ignoredSpanOrigins = listOf("auto.spring.webflux") + val filter = fixture.getSut(sentryTraceHeader = sentryTraceHeaderString, baggageHeaders = baggageHeaderStrings) + + withMockScopes { + filter.filter(fixture.exchange, fixture.chain).block() + + verify(fixture.chain).filter(fixture.exchange) + + verify(fixture.scopes, never()).captureTransaction( + anyOrNull(), + anyOrNull(), + anyOrNull(), + anyOrNull() + ) + + verify(fixture.scopes, never()).continueTrace(any(), any()) + } + } }