From 187a4764aa1a6a6cd95dccdd535d4c902faac579 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Thu, 28 Nov 2024 06:20:55 +0100 Subject: [PATCH 01/16] bump OTel to 2.10.0 --- buildSrc/src/main/java/Config.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/buildSrc/src/main/java/Config.kt b/buildSrc/src/main/java/Config.kt index dd623483425..5ec54e22d6e 100644 --- a/buildSrc/src/main/java/Config.kt +++ b/buildSrc/src/main/java/Config.kt @@ -156,11 +156,11 @@ object Config { val sentryNativeNdk = "io.sentry:sentry-native-ndk:0.7.14" object OpenTelemetry { - val otelVersion = "1.41.0" + val otelVersion = "1.44.1" val otelAlphaVersion = "$otelVersion-alpha" - val otelInstrumentationVersion = "2.7.0" + val otelInstrumentationVersion = "2.10.0" val otelInstrumentationAlphaVersion = "$otelInstrumentationVersion-alpha" - val otelSemanticConvetionsVersion = "1.25.0-alpha" // check https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/dependencyManagement/build.gradle.kts#L49 for release version above to find a compatible version + val otelSemanticConvetionsVersion = "1.28.0-alpha" // check https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/dependencyManagement/build.gradle.kts#L49 for release version above to find a compatible version val otelSdk = "io.opentelemetry:opentelemetry-sdk:$otelVersion" val otelSemconv = "io.opentelemetry.semconv:opentelemetry-semconv:$otelSemanticConvetionsVersion" From 9a5dfc0ed525a681c1dd2b8d6de7c80f2b05a8ee Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Thu, 28 Nov 2024 12:52:23 +0100 Subject: [PATCH 02/16] support DB_QUERY_TEXT --- .../opentelemetry/SpanDescriptionExtractor.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java index 56acea238bd..2047bd37f80 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java @@ -96,7 +96,14 @@ private OtelSpanInfo descriptionForHttpMethod( private OtelSpanInfo descriptionForDbSystem(final @NotNull SpanData otelSpan) { final @NotNull Attributes attributes = otelSpan.getAttributes(); @Nullable String dbStatement = attributes.get(DbIncubatingAttributes.DB_STATEMENT); - @NotNull String description = dbStatement != null ? dbStatement : otelSpan.getName(); - return new OtelSpanInfo("db", description, TransactionNameSource.TASK); + if (dbStatement != null) { + return new OtelSpanInfo("db", dbStatement, TransactionNameSource.TASK); + } + @Nullable String dbQueryText = attributes.get(DbIncubatingAttributes.DB_QUERY_TEXT); + if (dbQueryText != null) { + return new OtelSpanInfo("db", dbQueryText, TransactionNameSource.TASK); + } + + return new OtelSpanInfo("db", otelSpan.getName(), TransactionNameSource.TASK); } } From c1b125755315d7ea2bbfa05e42a4aae6dceebaac Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Thu, 28 Nov 2024 12:53:04 +0100 Subject: [PATCH 03/16] changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0c11a9cb3e..2dab3467c11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ - Extract OpenTelemetry `URL_PATH` span attribute into description ([#3933](https://github.com/getsentry/sentry-java/pull/3933)) +### Dependencies + +- Bump OpenTelemetry to 1.44.1, OpenTelemetry Java Agent to 2.10.0 and Semantic Conventions to 1.28.0 ([#3935](https://github.com/getsentry/sentry-java/pull/3935)) + ## 8.0.0-beta.3 ### Features From ad9b417d5436b587da689014aa40fba2aa8e0c00 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Thu, 28 Nov 2024 13:59:12 +0100 Subject: [PATCH 04/16] change bom version for otel --- buildSrc/src/main/java/Config.kt | 1 + .../build.gradle.kts | 2 +- .../build.gradle.kts | 6 ------ .../build.gradle.kts | 6 ++++++ 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/buildSrc/src/main/java/Config.kt b/buildSrc/src/main/java/Config.kt index 5ec54e22d6e..e6ebe9f1938 100644 --- a/buildSrc/src/main/java/Config.kt +++ b/buildSrc/src/main/java/Config.kt @@ -170,6 +170,7 @@ object Config { val otelJavaAgentTooling = "io.opentelemetry.javaagent:opentelemetry-javaagent-tooling:$otelInstrumentationAlphaVersion" val otelExtensionAutoconfigureSpi = "io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:$otelVersion" val otelExtensionAutoconfigure = "io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:$otelVersion" + val otelInstrumentationBom = "io.opentelemetry.instrumentation:opentelemetry-instrumentation-bom:$otelInstrumentationVersion" } } diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry-noagent/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry-noagent/build.gradle.kts index e0f82ad781c..dfa17eb9522 100644 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry-noagent/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry-noagent/build.gradle.kts @@ -71,7 +71,7 @@ dependencies { dependencyManagement { imports { - mavenBom("io.opentelemetry.instrumentation:opentelemetry-instrumentation-bom:2.7.0") + mavenBom(Config.Libs.OpenTelemetry.otelInstrumentationBom) } } diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/build.gradle.kts index 2dd6f2574ba..fe5dec4415f 100644 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/build.gradle.kts @@ -68,12 +68,6 @@ dependencies { testImplementation(Config.Libs.apolloKotlin) } -dependencyManagement { - imports { - mavenBom("io.opentelemetry.instrumentation:opentelemetry-instrumentation-bom:2.7.0") - } -} - configure { test { java.srcDir("src/test/java") diff --git a/sentry-samples/sentry-samples-spring-boot-opentelemetry-noagent/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot-opentelemetry-noagent/build.gradle.kts index 2eab2bb10ee..a7a879f0dde 100644 --- a/sentry-samples/sentry-samples-spring-boot-opentelemetry-noagent/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot-opentelemetry-noagent/build.gradle.kts @@ -70,6 +70,12 @@ dependencies { testImplementation("org.apache.httpcomponents:httpclient") } +dependencyManagement { + imports { + mavenBom(Config.Libs.OpenTelemetry.otelInstrumentationBom) + } +} + configure { test { java.srcDir("src/test/java") From df3ae7b16dd46fc3b44dff172326d5d9d373d44c Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Fri, 29 Nov 2024 07:24:20 +0100 Subject: [PATCH 05/16] Replace OTel ContextStorage wrapper with ContextStorageProvider --- .../api/sentry-opentelemetry-bootstrap.api | 5 +++++ .../opentelemetry/OtelContextScopesStorage.java | 5 ++--- .../opentelemetry/SentryContextStorageProvider.java | 12 ++++++++++++ .../io.opentelemetry.context.ContextStorageProvider | 1 + 4 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryContextStorageProvider.java create mode 100644 sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/resources/META-INF/services/io.opentelemetry.context.ContextStorageProvider diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api index 0e061c84c09..38ce3b8803a 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api @@ -103,6 +103,11 @@ public final class io/sentry/opentelemetry/SentryContextStorage : io/opentelemet public fun current ()Lio/opentelemetry/context/Context; } +public final class io/sentry/opentelemetry/SentryContextStorageProvider : io/opentelemetry/context/ContextStorageProvider { + public fun ()V + public fun get ()Lio/opentelemetry/context/ContextStorage; +} + public final class io/sentry/opentelemetry/SentryContextWrapper : io/opentelemetry/context/Context { public fun get (Lio/opentelemetry/context/ContextKey;)Ljava/lang/Object; public fun toString ()Ljava/lang/String; diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java index cd631871784..143ebb6c16d 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java @@ -3,7 +3,6 @@ import static io.sentry.opentelemetry.SentryOtelKeys.SENTRY_SCOPES_KEY; import io.opentelemetry.context.Context; -import io.opentelemetry.context.ContextStorage; import io.opentelemetry.context.Scope; import io.sentry.IScopes; import io.sentry.IScopesStorage; @@ -27,8 +26,8 @@ public void init() { * should try to use OTels StorageProvider mechanism instead. */ // ContextStorage.addWrapper((storage) -> new SentryContextStorage(storage)); - ContextStorage.addWrapper( - (storage) -> new SentryContextStorage(new SentryOtelThreadLocalStorage())); + // ContextStorage.addWrapper( + // (storage) -> new SentryContextStorage(new SentryOtelThreadLocalStorage())); } @Override diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryContextStorageProvider.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryContextStorageProvider.java new file mode 100644 index 00000000000..42fc8a202d2 --- /dev/null +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryContextStorageProvider.java @@ -0,0 +1,12 @@ +package io.sentry.opentelemetry; + +import io.opentelemetry.context.ContextStorage; +import io.opentelemetry.context.ContextStorageProvider; + +public final class SentryContextStorageProvider implements ContextStorageProvider { + @Override + public ContextStorage get() { + System.out.println("hello from SentryContextStorageProvider"); + return new SentryContextStorage(new SentryOtelThreadLocalStorage()); + } +} diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/resources/META-INF/services/io.opentelemetry.context.ContextStorageProvider b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/resources/META-INF/services/io.opentelemetry.context.ContextStorageProvider new file mode 100644 index 00000000000..87a43eea486 --- /dev/null +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/resources/META-INF/services/io.opentelemetry.context.ContextStorageProvider @@ -0,0 +1 @@ +io.sentry.opentelemetry.SentryContextStorageProvider From 96314d99d6055c6d971eb632da3d71e43c54e8a2 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Fri, 29 Nov 2024 09:25:10 +0100 Subject: [PATCH 06/16] bump spring boot 3.4 --- buildSrc/src/main/java/Config.kt | 2 +- .../apollo3/SentryApollo3InterceptorTest.kt | 10 ++++---- ...ntryApollo3InterceptorWithVariablesTest.kt | 4 +++- .../apollo/SentryApolloInterceptorTest.kt | 6 +++-- .../okhttp/SentryOkHttpEventListenerTest.kt | 6 +++-- .../okhttp/SentryOkHttpInterceptorTest.kt | 16 +++++++------ .../sentry/openfeign/SentryFeignClientTest.kt | 12 ++++++---- .../SentrySpanRestClientCustomizerTest.kt | 24 ++++++++++++------- .../SentrySpanRestTemplateCustomizerTest.kt | 12 ++++++---- .../SentrySpanWebClientCustomizerTest.kt | 10 ++++---- .../SentrySpanRestTemplateCustomizerTest.kt | 12 ++++++---- .../boot/SentrySpanWebClientCustomizerTest.kt | 10 ++++---- .../api/sentry-test-support.api | 1 + .../src/main/kotlin/io/sentry/Assertions.kt | 2 ++ 14 files changed, 79 insertions(+), 48 deletions(-) diff --git a/buildSrc/src/main/java/Config.kt b/buildSrc/src/main/java/Config.kt index e6ebe9f1938..5b4e907e298 100644 --- a/buildSrc/src/main/java/Config.kt +++ b/buildSrc/src/main/java/Config.kt @@ -7,7 +7,7 @@ object Config { val kotlinStdLib = "stdlib-jdk8" val springBootVersion = "2.7.5" - val springBoot3Version = "3.3.2" + val springBoot3Version = "3.4.0" val kotlinCompatibleLanguageVersion = "1.4" val composeVersion = "1.5.3" 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 3b836f45b96..e5cb93eb3da 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 @@ -24,6 +24,7 @@ import io.sentry.TraceContext import io.sentry.TracesSamplingDecision import io.sentry.TransactionContext import io.sentry.apollo3.SentryApollo3HttpInterceptor.BeforeSpanCallback +import io.sentry.mockServerRequestTimeoutMillis import io.sentry.protocol.SdkVersion import io.sentry.protocol.SentryTransaction import io.sentry.util.Apollo3PlatformTestManipulator @@ -40,6 +41,7 @@ import org.mockito.kotlin.doAnswer import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.whenever +import java.util.concurrent.TimeUnit import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull @@ -192,7 +194,7 @@ class SentryApollo3InterceptorTest { fixture.options.setTracePropagationTargets(listOf("some-host-that-does-not-exist")) executeQuery(isSpanActive = false) - val recorderRequest = fixture.server.takeRequest() + val recorderRequest = fixture.server.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) assertNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) } @@ -201,7 +203,7 @@ class SentryApollo3InterceptorTest { fun `when there is no active span, does not add sentry trace header to the request`() { executeQuery(isSpanActive = false) - val recorderRequest = fixture.server.takeRequest() + val recorderRequest = fixture.server.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNotNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) assertNotNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) } @@ -209,7 +211,7 @@ class SentryApollo3InterceptorTest { @Test fun `when there is an active span, adds sentry trace headers to the request`() { executeQuery() - val recorderRequest = fixture.server.takeRequest() + val recorderRequest = fixture.server.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNotNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) assertNotNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) } @@ -217,7 +219,7 @@ class SentryApollo3InterceptorTest { @Test fun `when there is an active span, existing baggage headers are merged with sentry baggage into single header`() { executeQuery(sut = fixture.getSut(addThirdPartyBaggageHeader = true)) - val recorderRequest = fixture.server.takeRequest() + val recorderRequest = fixture.server.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNotNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) assertNotNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) diff --git a/sentry-apollo-3/src/test/java/io/sentry/apollo3/SentryApollo3InterceptorWithVariablesTest.kt b/sentry-apollo-3/src/test/java/io/sentry/apollo3/SentryApollo3InterceptorWithVariablesTest.kt index 3ac3d80d7dd..dc9a94f184d 100644 --- a/sentry-apollo-3/src/test/java/io/sentry/apollo3/SentryApollo3InterceptorWithVariablesTest.kt +++ b/sentry-apollo-3/src/test/java/io/sentry/apollo3/SentryApollo3InterceptorWithVariablesTest.kt @@ -12,6 +12,7 @@ import io.sentry.TraceContext import io.sentry.TracesSamplingDecision import io.sentry.TransactionContext import io.sentry.apollo3.SentryApollo3HttpInterceptor.BeforeSpanCallback +import io.sentry.mockServerRequestTimeoutMillis import io.sentry.protocol.SentryTransaction import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking @@ -23,6 +24,7 @@ import org.mockito.kotlin.check import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.whenever +import java.util.concurrent.TimeUnit import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull @@ -153,7 +155,7 @@ class SentryApollo3InterceptorWithVariablesTest { @Test fun `internal headers are not sent over the wire`() { executeQuery(fixture.getSut()) - val recorderRequest = fixture.server.takeRequest() + val recorderRequest = fixture.server.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNull(recorderRequest.headers[SentryApollo3HttpInterceptor.SENTRY_APOLLO_3_VARIABLES]) assertNull(recorderRequest.headers[SentryApollo3HttpInterceptor.SENTRY_APOLLO_3_OPERATION_TYPE]) } 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 b1b118c3345..1c56af13bd1 100644 --- a/sentry-apollo/src/test/java/io/sentry/apollo/SentryApolloInterceptorTest.kt +++ b/sentry-apollo/src/test/java/io/sentry/apollo/SentryApolloInterceptorTest.kt @@ -17,6 +17,7 @@ import io.sentry.SpanStatus import io.sentry.TraceContext import io.sentry.TracesSamplingDecision import io.sentry.TransactionContext +import io.sentry.mockServerRequestTimeoutMillis import io.sentry.protocol.SdkVersion import io.sentry.protocol.SentryTransaction import io.sentry.util.ApolloPlatformTestManipulator @@ -33,6 +34,7 @@ import org.mockito.kotlin.doAnswer import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.whenever +import java.util.concurrent.TimeUnit import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull @@ -154,7 +156,7 @@ class SentryApolloInterceptorTest { fun `when there is no active span, adds sentry trace header to the request from scope`() { executeQuery(isSpanActive = false) - val recorderRequest = fixture.server.takeRequest() + val recorderRequest = fixture.server.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNotNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) assertNotNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) } @@ -162,7 +164,7 @@ class SentryApolloInterceptorTest { @Test fun `when there is an active span, adds sentry trace headers to the request`() { executeQuery() - val recorderRequest = fixture.server.takeRequest() + val recorderRequest = fixture.server.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNotNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) assertNotNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) } diff --git a/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpEventListenerTest.kt b/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpEventListenerTest.kt index ab179189b4b..b1e5fb52262 100644 --- a/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpEventListenerTest.kt +++ b/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpEventListenerTest.kt @@ -8,6 +8,7 @@ import io.sentry.SentryTracer import io.sentry.SpanDataConvention import io.sentry.SpanStatus import io.sentry.TransactionContext +import io.sentry.mockServerRequestTimeoutMillis import okhttp3.Call import okhttp3.EventListener import okhttp3.OkHttpClient @@ -25,6 +26,7 @@ import org.mockito.kotlin.mock import org.mockito.kotlin.spy import org.mockito.kotlin.verify import org.mockito.kotlin.whenever +import java.util.concurrent.TimeUnit import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull @@ -111,7 +113,7 @@ class SentryOkHttpEventListenerTest { fun `when there is an active span and the SentryOkHttpInterceptor, adds sentry trace headers to the request`() { val sut = fixture.getSut(useInterceptor = true) sut.newCall(getRequest()).execute() - val recorderRequest = fixture.server.takeRequest() + val recorderRequest = fixture.server.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNotNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) assertNotNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) } @@ -121,7 +123,7 @@ class SentryOkHttpEventListenerTest { fun `when there is an active span but no SentryOkHttpInterceptor, sentry trace headers are not added to the request`() { val sut = fixture.getSut() sut.newCall(getRequest()).execute() - val recorderRequest = fixture.server.takeRequest() + val recorderRequest = fixture.server.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) assertNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) } 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 8d2feff06ef..4bda92f3c48 100644 --- a/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpInterceptorTest.kt +++ b/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpInterceptorTest.kt @@ -19,6 +19,7 @@ import io.sentry.SpanStatus import io.sentry.TransactionContext import io.sentry.TypeCheckHint import io.sentry.exception.SentryHttpClientException +import io.sentry.mockServerRequestTimeoutMillis import okhttp3.Interceptor import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient @@ -37,6 +38,7 @@ import org.mockito.kotlin.never import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import java.io.IOException +import java.util.concurrent.TimeUnit import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse @@ -159,7 +161,7 @@ class SentryOkHttpInterceptorTest { fun `when there is an active span and server is listed in tracing origins, adds sentry trace headers to the request`() { val sut = fixture.getSut() sut.newCall(getRequest()).execute() - val recorderRequest = fixture.server.takeRequest() + val recorderRequest = fixture.server.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNotNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) assertNotNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) } @@ -170,7 +172,7 @@ class SentryOkHttpInterceptorTest { val sut = fixture.getSut(keepDefaultTracePropagationTargets = true) sut.newCall(getRequest()).execute() - val recorderRequest = fixture.server.takeRequest() + val recorderRequest = fixture.server.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNotNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) assertNotNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) } @@ -180,7 +182,7 @@ class SentryOkHttpInterceptorTest { fun `when there is an active span and server is not listed in tracing origins, does not add sentry trace headers to the request`() { val sut = fixture.getSut(includeMockServerInTracePropagationTargets = false) sut.newCall(Request.Builder().get().url(fixture.server.url("/hello")).build()).execute() - val recorderRequest = fixture.server.takeRequest() + val recorderRequest = fixture.server.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) assertNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) } @@ -191,7 +193,7 @@ class SentryOkHttpInterceptorTest { val sut = fixture.getSut() fixture.options.setTracePropagationTargets(emptyList()) sut.newCall(Request.Builder().get().url(fixture.server.url("/hello")).build()).execute() - val recorderRequest = fixture.server.takeRequest() + val recorderRequest = fixture.server.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) assertNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) } @@ -200,7 +202,7 @@ class SentryOkHttpInterceptorTest { fun `when there is no active span, adds sentry trace header to the request from scope`() { val sut = fixture.getSut(isSpanActive = false) sut.newCall(getRequest()).execute() - val recorderRequest = fixture.server.takeRequest() + val recorderRequest = fixture.server.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNotNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) assertNotNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) } @@ -210,7 +212,7 @@ class SentryOkHttpInterceptorTest { val sut = fixture.getSut(isSpanActive = false) fixture.options.setTracePropagationTargets(listOf("some-host-that-does-not-exist")) sut.newCall(getRequest()).execute() - val recorderRequest = fixture.server.takeRequest() + val recorderRequest = fixture.server.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) assertNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) } @@ -219,7 +221,7 @@ class SentryOkHttpInterceptorTest { fun `when there is an active span, existing baggage headers are merged with sentry baggage into single header`() { val sut = fixture.getSut() sut.newCall(getRequestWithBaggageHeader()).execute() - val recorderRequest = fixture.server.takeRequest() + val recorderRequest = fixture.server.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNotNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) assertNotNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) 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 959b890d460..28d65da4f18 100644 --- a/sentry-openfeign/src/test/kotlin/io/sentry/openfeign/SentryFeignClientTest.kt +++ b/sentry-openfeign/src/test/kotlin/io/sentry/openfeign/SentryFeignClientTest.kt @@ -16,6 +16,7 @@ import io.sentry.SentryTracer import io.sentry.SpanDataConvention import io.sentry.SpanStatus import io.sentry.TransactionContext +import io.sentry.mockServerRequestTimeoutMillis import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.MockWebServer import org.mockito.kotlin.any @@ -25,6 +26,7 @@ import org.mockito.kotlin.doAnswer import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.whenever +import java.util.concurrent.TimeUnit import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals @@ -93,7 +95,7 @@ class SentryFeignClientTest { fixture.sentryOptions.dsn = "https://key@sentry.io/proj" val sut = fixture.getSut() sut.getOk() - val recorderRequest = fixture.server.takeRequest() + val recorderRequest = fixture.server.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNotNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) assertNotNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) } @@ -106,7 +108,7 @@ class SentryFeignClientTest { sut.getOkWithBaggageHeader(mapOf("baggage" to listOf("thirdPartyBaggage=someValue", "secondThirdPartyBaggage=secondValue; property;propertyKey=propertyValue,anotherThirdPartyBaggage=anotherValue"))) - val recorderRequest = fixture.server.takeRequest() + val recorderRequest = fixture.server.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNotNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) assertNotNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) @@ -123,7 +125,7 @@ class SentryFeignClientTest { fixture.sentryOptions.dsn = "https://key@sentry.io/proj" val sut = fixture.getSut(isSpanActive = false) sut.getOk() - val recorderRequest = fixture.server.takeRequest() + val recorderRequest = fixture.server.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNotNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) assertNotNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) } @@ -134,7 +136,7 @@ class SentryFeignClientTest { fixture.sentryOptions.dsn = "https://key@sentry.io/proj" val sut = fixture.getSut(isSpanActive = false) sut.getOk() - val recorderRequest = fixture.server.takeRequest() + val recorderRequest = fixture.server.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) assertNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) } @@ -146,7 +148,7 @@ class SentryFeignClientTest { fixture.sentryOptions.dsn = "https://key@sentry.io/proj" val sut = fixture.getSut() sut.getOk() - val recorderRequest = fixture.server.takeRequest() + val recorderRequest = fixture.server.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) assertNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) } 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 9ba9bf57d28..545729be335 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 @@ -11,6 +11,7 @@ import io.sentry.SentryTracer import io.sentry.SpanStatus import io.sentry.TracesSamplingDecision import io.sentry.TransactionContext +import io.sentry.mockServerRequestTimeoutMillis import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.MockWebServer import okhttp3.mockwebserver.SocketPolicy @@ -28,12 +29,14 @@ import org.springframework.http.HttpStatus import org.springframework.http.client.HttpComponentsClientHttpRequestFactory import org.springframework.web.client.RestClient import java.time.Duration +import java.util.concurrent.TimeUnit import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull import kotlin.test.assertTrue class SentrySpanRestClientCustomizerTest { + class Fixture { val sentryOptions = SentryOptions() val scopes = mock() @@ -104,7 +107,7 @@ class SentrySpanRestClientCustomizerTest { assertThat(span.description).isEqualTo("GET ${fixture.url}") assertThat(span.status).isEqualTo(SpanStatus.OK) - val recordedRequest = fixture.mockServer.takeRequest() + val recordedRequest = fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertThat(recordedRequest.headers["sentry-trace"]!!).startsWith(fixture.transaction.spanContext.traceId.toString()) .endsWith("-1") .doesNotContain(fixture.transaction.spanContext.spanId.toString()) @@ -128,7 +131,7 @@ class SentrySpanRestClientCustomizerTest { .retrieve() .toEntity(String::class.java) - val recorderRequest = fixture.mockServer.takeRequest() + val recorderRequest = fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNotNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) assertNotNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) @@ -147,7 +150,7 @@ class SentrySpanRestClientCustomizerTest { .uri(fixture.url) .retrieve() .toEntity(String::class.java) - val recordedRequest = fixture.mockServer.takeRequest() + val recordedRequest = fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertThat(recordedRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]).isNull() } @@ -159,7 +162,7 @@ class SentrySpanRestClientCustomizerTest { .uri(fixture.url) .retrieve() .toEntity(String::class.java) - val recordedRequest = fixture.mockServer.takeRequest() + val recordedRequest = fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertThat(recordedRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]).isNotNull() } @@ -184,13 +187,18 @@ class SentrySpanRestClientCustomizerTest { @Test fun `when transaction is active and throws IO exception, creates span with error status around RestClient HTTP call`() { try { - fixture.getSut(isTransactionActive = true, socketPolicy = SocketPolicy.DISCONNECT_AT_START).build() + val sut = fixture.getSut( + isTransactionActive = true, + socketPolicy = SocketPolicy.DISCONNECT_AT_START + ).build() + sut .get() .uri(fixture.url) .retrieve() - } catch (_: Throwable) { + } catch (t: Throwable) { + println(t) } - fixture.mockServer.takeRequest() +// fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertThat(fixture.transaction.spans).hasSize(1) val span = fixture.transaction.spans.first() assertThat(span.operation).isEqualTo("http.client") @@ -227,7 +235,7 @@ class SentrySpanRestClientCustomizerTest { .retrieve() .toEntity(String::class.java) - val recorderRequest = fixture.mockServer.takeRequest() + val recorderRequest = fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNotNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) assertNotNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) 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 4ab3205b319..f6fe3b69bc9 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 @@ -11,6 +11,7 @@ import io.sentry.SentryTracer import io.sentry.SpanStatus import io.sentry.TracesSamplingDecision import io.sentry.TransactionContext +import io.sentry.mockServerRequestTimeoutMillis import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.MockWebServer import okhttp3.mockwebserver.SocketPolicy @@ -29,6 +30,7 @@ import org.springframework.http.HttpMethod import org.springframework.http.HttpStatus import org.springframework.web.client.RestTemplate import java.time.Duration +import java.util.concurrent.TimeUnit import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull @@ -94,7 +96,7 @@ class SentrySpanRestTemplateCustomizerTest { assertThat(span.description).isEqualTo("GET ${fixture.url}") assertThat(span.status).isEqualTo(SpanStatus.OK) - val recordedRequest = fixture.mockServer.takeRequest() + val recordedRequest = fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertThat(recordedRequest.headers["sentry-trace"]!!).startsWith(fixture.transaction.spanContext.traceId.toString()) .endsWith("-1") .doesNotContain(fixture.transaction.spanContext.spanId.toString()) @@ -112,7 +114,7 @@ class SentrySpanRestTemplateCustomizerTest { sut.exchange(fixture.url, HttpMethod.GET, requestEntity, String::class.java) - val recorderRequest = fixture.mockServer.takeRequest() + val recorderRequest = fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNotNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) assertNotNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) @@ -128,7 +130,7 @@ class SentrySpanRestTemplateCustomizerTest { fun `when transaction is active and server is not listed in tracing origins, does not add sentry trace header to the request`() { fixture.getSut(isTransactionActive = true, includeMockServerInTracingOrigins = false) .getForObject(fixture.url, String::class.java) - val recordedRequest = fixture.mockServer.takeRequest() + val recordedRequest = fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertThat(recordedRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]).isNull() } @@ -136,7 +138,7 @@ class SentrySpanRestTemplateCustomizerTest { fun `when transaction is active and server is listed in tracing origins, adds sentry trace header to the request`() { fixture.getSut(isTransactionActive = true, includeMockServerInTracingOrigins = true) .getForObject(fixture.url, String::class.java) - val recordedRequest = fixture.mockServer.takeRequest() + val recordedRequest = fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertThat(recordedRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]).isNotNull() } @@ -185,7 +187,7 @@ class SentrySpanRestTemplateCustomizerTest { sut.exchange(fixture.url, HttpMethod.GET, requestEntity, String::class.java) - val recorderRequest = fixture.mockServer.takeRequest() + val recorderRequest = fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNotNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) assertNotNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) 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 d3fb5f7d31c..decbb68e9ee 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 @@ -12,6 +12,7 @@ import io.sentry.SentryTracer import io.sentry.SpanStatus import io.sentry.TracesSamplingDecision import io.sentry.TransactionContext +import io.sentry.mockServerRequestTimeoutMillis import okhttp3.mockwebserver.Dispatcher import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.MockWebServer @@ -30,6 +31,7 @@ import org.springframework.http.HttpStatus import org.springframework.http.MediaType import org.springframework.web.reactive.function.BodyInserters import org.springframework.web.reactive.function.client.WebClient +import java.util.concurrent.TimeUnit import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull @@ -130,7 +132,7 @@ class SentrySpanWebClientCustomizerTest { .retrieve() .bodyToMono(String::class.java) .block() - val recordedRequest = fixture.mockServer.takeRequest() + val recordedRequest = fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNull(recordedRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) assertNull(recordedRequest.headers[BaggageHeader.BAGGAGE_HEADER]) } @@ -143,7 +145,7 @@ class SentrySpanWebClientCustomizerTest { .retrieve() .bodyToMono(String::class.java) .block() - val recordedRequest = fixture.mockServer.takeRequest() + val recordedRequest = fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNull(recordedRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) assertNull(recordedRequest.headers[BaggageHeader.BAGGAGE_HEADER]) } @@ -156,7 +158,7 @@ class SentrySpanWebClientCustomizerTest { .retrieve() .bodyToMono(String::class.java) .block() - val recordedRequest = fixture.mockServer.takeRequest() + val recordedRequest = fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNotNull(recordedRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) assertNotNull(recordedRequest.headers[BaggageHeader.BAGGAGE_HEADER]) } @@ -169,7 +171,7 @@ class SentrySpanWebClientCustomizerTest { .retrieve() .bodyToMono(String::class.java) .block() - val recordedRequest = fixture.mockServer.takeRequest() + val recordedRequest = fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNotNull(recordedRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) assertNotNull(recordedRequest.headers[BaggageHeader.BAGGAGE_HEADER]) } 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 33d7974d8ab..5d09db87d56 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 @@ -11,6 +11,7 @@ import io.sentry.SentryTracer import io.sentry.SpanStatus import io.sentry.TracesSamplingDecision import io.sentry.TransactionContext +import io.sentry.mockServerRequestTimeoutMillis import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.MockWebServer import okhttp3.mockwebserver.SocketPolicy @@ -29,6 +30,7 @@ import org.springframework.http.HttpMethod import org.springframework.http.HttpStatus import org.springframework.web.client.RestTemplate import java.time.Duration +import java.util.concurrent.TimeUnit import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull @@ -96,7 +98,7 @@ class SentrySpanRestTemplateCustomizerTest { assertThat(span.description).isEqualTo("GET ${fixture.url}") assertThat(span.status).isEqualTo(SpanStatus.OK) - val recordedRequest = fixture.mockServer.takeRequest() + val recordedRequest = fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertThat(recordedRequest.headers["sentry-trace"]!!).startsWith(fixture.transaction.spanContext.traceId.toString()) .endsWith("-1") .doesNotContain(fixture.transaction.spanContext.spanId.toString()) @@ -114,7 +116,7 @@ class SentrySpanRestTemplateCustomizerTest { sut.exchange(fixture.url, HttpMethod.GET, requestEntity, String::class.java) - val recorderRequest = fixture.mockServer.takeRequest() + val recorderRequest = fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNotNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) assertNotNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) @@ -130,7 +132,7 @@ class SentrySpanRestTemplateCustomizerTest { fun `when transaction is active and server is not listed in tracing origins, does not add sentry trace header to the request`() { fixture.getSut(isTransactionActive = true, includeMockServerInTracingOrigins = false) .getForObject(fixture.url, String::class.java) - val recordedRequest = fixture.mockServer.takeRequest() + val recordedRequest = fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertThat(recordedRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]).isNull() } @@ -138,7 +140,7 @@ class SentrySpanRestTemplateCustomizerTest { fun `when transaction is active and server is listed in tracing origins, adds sentry trace header to the request`() { fixture.getSut(isTransactionActive = true, includeMockServerInTracingOrigins = true) .getForObject(fixture.url, String::class.java) - val recordedRequest = fixture.mockServer.takeRequest() + val recordedRequest = fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertThat(recordedRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]).isNotNull() } @@ -187,7 +189,7 @@ class SentrySpanRestTemplateCustomizerTest { sut.exchange(fixture.url, HttpMethod.GET, requestEntity, String::class.java) - val recorderRequest = fixture.mockServer.takeRequest() + val recorderRequest = fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNotNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) assertNotNull(recorderRequest.headers[BaggageHeader.BAGGAGE_HEADER]) 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 4f1f70d75ee..ac63805436b 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 @@ -12,6 +12,7 @@ import io.sentry.SentryTracer import io.sentry.SpanStatus import io.sentry.TracesSamplingDecision import io.sentry.TransactionContext +import io.sentry.mockServerRequestTimeoutMillis import okhttp3.mockwebserver.Dispatcher import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.MockWebServer @@ -30,6 +31,7 @@ import org.springframework.http.HttpStatus import org.springframework.http.MediaType import org.springframework.web.reactive.function.BodyInserters import org.springframework.web.reactive.function.client.WebClient +import java.util.concurrent.TimeUnit import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull @@ -132,7 +134,7 @@ class SentrySpanWebClientCustomizerTest { .retrieve() .bodyToMono(String::class.java) .block() - val recordedRequest = fixture.mockServer.takeRequest() + val recordedRequest = fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNull(recordedRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) assertNull(recordedRequest.headers[BaggageHeader.BAGGAGE_HEADER]) } @@ -145,7 +147,7 @@ class SentrySpanWebClientCustomizerTest { .retrieve() .bodyToMono(String::class.java) .block() - val recordedRequest = fixture.mockServer.takeRequest() + val recordedRequest = fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNull(recordedRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) assertNull(recordedRequest.headers[BaggageHeader.BAGGAGE_HEADER]) } @@ -158,7 +160,7 @@ class SentrySpanWebClientCustomizerTest { .retrieve() .bodyToMono(String::class.java) .block() - val recordedRequest = fixture.mockServer.takeRequest() + val recordedRequest = fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNotNull(recordedRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) assertNotNull(recordedRequest.headers[BaggageHeader.BAGGAGE_HEADER]) } @@ -171,7 +173,7 @@ class SentrySpanWebClientCustomizerTest { .retrieve() .bodyToMono(String::class.java) .block() - val recordedRequest = fixture.mockServer.takeRequest() + val recordedRequest = fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertNotNull(recordedRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) assertNotNull(recordedRequest.headers[BaggageHeader.BAGGAGE_HEADER]) } diff --git a/sentry-test-support/api/sentry-test-support.api b/sentry-test-support/api/sentry-test-support.api index 27d70346902..8ef2e78743f 100644 --- a/sentry-test-support/api/sentry-test-support.api +++ b/sentry-test-support/api/sentry-test-support.api @@ -5,6 +5,7 @@ public final class io/sentry/AssertionsKt { public static synthetic fun assertEnvelopeTransaction$default (Ljava/util/List;Lio/sentry/ILogger;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lio/sentry/protocol/SentryTransaction; public static final fun checkEvent (Lkotlin/jvm/functions/Function1;)Lio/sentry/SentryEnvelope; public static final fun checkTransaction (Lkotlin/jvm/functions/Function1;)Lio/sentry/SentryEnvelope; + public static final fun getMockServerRequestTimeoutMillis ()J } public final class io/sentry/SkipError : java/lang/Error { diff --git a/sentry-test-support/src/main/kotlin/io/sentry/Assertions.kt b/sentry-test-support/src/main/kotlin/io/sentry/Assertions.kt index 435030ea3b5..6d75810ecc5 100644 --- a/sentry-test-support/src/main/kotlin/io/sentry/Assertions.kt +++ b/sentry-test-support/src/main/kotlin/io/sentry/Assertions.kt @@ -99,3 +99,5 @@ If you are using `check` as part of a stubbing, use `argThat` or `argForWhich` i } class SkipError(message: String) : Error(message) + +val mockServerRequestTimeoutMillis = 5000L From 6794a58c5feb130a2ee4f38e7159c1e6408f5350 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Fri, 29 Nov 2024 12:31:18 +0100 Subject: [PATCH 07/16] fix twp --- .../io/sentry/systemtest/PersonSystemTest.kt | 66 +++++++++++++++++++ .../sentry/systemtest/util/RestTestClient.kt | 13 ++-- .../SentrySpanRestClientCustomizerTest.kt | 4 +- .../java/io/sentry/TransactionContext.java | 11 ++-- .../java/io/sentry/util/TracingUtils.java | 4 +- sentry/src/test/java/io/sentry/ScopesTest.kt | 17 +++++ .../java/io/sentry/util/TracingUtilsTest.kt | 34 ++++++++++ 7 files changed, 137 insertions(+), 12 deletions(-) diff --git a/sentry-samples/sentry-samples-spring-boot-opentelemetry/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-opentelemetry/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt index 9309529ce44..2c1b24ba3bb 100644 --- a/sentry-samples/sentry-samples-spring-boot-opentelemetry/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt +++ b/sentry-samples/sentry-samples-spring-boot-opentelemetry/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt @@ -44,4 +44,70 @@ class PersonSystemTest { testHelper.doesTransactionContainSpanWithOp(transaction, "spanCreatedThroughSentryApi") } } + + @Test + fun `create person creates transaction if no sampled flag in sentry-trace header`() { + val restClient = testHelper.restClient + val person = Person("firstA", "lastB") + val returnedPerson = restClient.createPerson( + person, + mapOf( + "sentry-trace" to "f9118105af4a2d42b4124532cd1065ff-424cffc8f94feeee", + "baggage" to "sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-trace_id=f9118105af4a2d42b4124532cd1065ff,sentry-transaction=HTTP%20GET" + ) + ) + assertEquals(HttpStatus.OK, restClient.lastKnownStatusCode) + + assertEquals(person.firstName, returnedPerson!!.firstName) + assertEquals(person.lastName, returnedPerson!!.lastName) + + testHelper.ensureTransactionReceived { transaction -> + testHelper.doesTransactionContainSpanWithOp(transaction, "spanCreatedThroughOtelApi") && + testHelper.doesTransactionContainSpanWithOp(transaction, "spanCreatedThroughSentryApi") + } + } + + @Test + fun `create person creates transaction if sampled true in sentry-trace header`() { + val restClient = testHelper.restClient + val person = Person("firstA", "lastB") + val returnedPerson = restClient.createPerson( + person, + mapOf( + "sentry-trace" to "f9118105af4a2d42b4124532cd1065ff-424cffc8f94feeee-1", + "baggage" to "sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-trace_id=f9118105af4a2d42b4124532cd1065ff,sentry-transaction=HTTP%20GET" + ) + ) + assertEquals(HttpStatus.OK, restClient.lastKnownStatusCode) + + assertEquals(person.firstName, returnedPerson!!.firstName) + assertEquals(person.lastName, returnedPerson!!.lastName) + + testHelper.ensureTransactionReceived { transaction -> + testHelper.doesTransactionContainSpanWithOp(transaction, "spanCreatedThroughOtelApi") && + testHelper.doesTransactionContainSpanWithOp(transaction, "spanCreatedThroughSentryApi") + } + } + + @Test + fun `create person does not create transaction if sampled false in sentry-trace header`() { + val restClient = testHelper.restClient + val person = Person("firstA", "lastB") + val returnedPerson = restClient.createPerson( + person, + mapOf( + "sentry-trace" to "f9118105af4a2d42b4124532cd1065ff-424cffc8f94feeee-0", + "baggage" to "sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-trace_id=f9118105af4a2d42b4124532cd1065ff,sentry-transaction=HTTP%20GET" + ) + ) + assertEquals(HttpStatus.OK, restClient.lastKnownStatusCode) + + assertEquals(person.firstName, returnedPerson!!.firstName) + assertEquals(person.lastName, returnedPerson!!.lastName) + + testHelper.ensureTransactionReceived { transaction -> + testHelper.doesTransactionContainSpanWithOp(transaction, "spanCreatedThroughOtelApi") && + testHelper.doesTransactionContainSpanWithOp(transaction, "spanCreatedThroughSentryApi") + } + } } diff --git a/sentry-samples/sentry-samples-spring-boot-opentelemetry/src/test/kotlin/io/sentry/systemtest/util/RestTestClient.kt b/sentry-samples/sentry-samples-spring-boot-opentelemetry/src/test/kotlin/io/sentry/systemtest/util/RestTestClient.kt index f5d5bd7ee38..1c4eef63f78 100644 --- a/sentry-samples/sentry-samples-spring-boot-opentelemetry/src/test/kotlin/io/sentry/systemtest/util/RestTestClient.kt +++ b/sentry-samples/sentry-samples-spring-boot-opentelemetry/src/test/kotlin/io/sentry/systemtest/util/RestTestClient.kt @@ -22,9 +22,9 @@ class RestTestClient(private val backendBaseUrl: String) : LoggingInsecureRestCl } } - fun createPerson(person: Person): Person? { + fun createPerson(person: Person, extraHeaders: Map? = null): Person? { return try { - val response = restTemplate().exchange("$backendBaseUrl/person/", HttpMethod.POST, entityWithAuth(person), Person::class.java, person) + val response = restTemplate().exchange("$backendBaseUrl/person/", HttpMethod.POST, entityWithAuth(person, extraHeaders), Person::class.java, person) lastKnownStatusCode = response.statusCode response.body } catch (e: HttpStatusCodeException) { @@ -55,9 +55,12 @@ class RestTestClient(private val backendBaseUrl: String) : LoggingInsecureRestCl } } - private fun entityWithAuth(request: Any? = null): HttpEntity { - val headers = HttpHeaders().also { - it.setBasicAuth("user", "password") + private fun entityWithAuth(request: Any? = null, extraHeaders: Map? = null): HttpEntity { + val headers = HttpHeaders().also { httpHeaders -> + httpHeaders.setBasicAuth("user", "password") + extraHeaders?.forEach { key, value -> + httpHeaders.set(key, value) + } } return HttpEntity(request, headers) 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 545729be335..d32885b41fa 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 @@ -28,6 +28,7 @@ import org.springframework.http.HttpHeaders import org.springframework.http.HttpStatus import org.springframework.http.client.HttpComponentsClientHttpRequestFactory import org.springframework.web.client.RestClient +import org.springframework.web.client.toEntity import java.time.Duration import java.util.concurrent.TimeUnit import kotlin.test.Test @@ -195,10 +196,11 @@ class SentrySpanRestClientCustomizerTest { .get() .uri(fixture.url) .retrieve() + .toEntity(String::class.java) } catch (t: Throwable) { println(t) } -// fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! + fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! assertThat(fixture.transaction.spans).hasSize(1) val span = fixture.transaction.spans.first() assertThat(span.operation).isEqualTo("http.client") diff --git a/sentry/src/main/java/io/sentry/TransactionContext.java b/sentry/src/main/java/io/sentry/TransactionContext.java index 287fcee1fa5..aec0b8927de 100644 --- a/sentry/src/main/java/io/sentry/TransactionContext.java +++ b/sentry/src/main/java/io/sentry/TransactionContext.java @@ -30,11 +30,12 @@ public static TransactionContext fromPropagationContext( baggage.freeze(); Double sampleRate = baggage.getSampleRateDouble(); - Boolean sampled = parentSampled != null ? parentSampled.booleanValue() : false; - if (sampleRate != null) { - samplingDecision = new TracesSamplingDecision(sampled, sampleRate); - } else { - samplingDecision = new TracesSamplingDecision(sampled); + if (parentSampled != null) { + if (sampleRate != null) { + samplingDecision = new TracesSamplingDecision(parentSampled.booleanValue(), sampleRate); + } else { + samplingDecision = new TracesSamplingDecision(parentSampled.booleanValue()); + } } } diff --git a/sentry/src/main/java/io/sentry/util/TracingUtils.java b/sentry/src/main/java/io/sentry/util/TracingUtils.java index 5f09a674574..2f0cc4a98ec 100644 --- a/sentry/src/main/java/io/sentry/util/TracingUtils.java +++ b/sentry/src/main/java/io/sentry/util/TracingUtils.java @@ -65,7 +65,9 @@ public static void startNewTrace(final @NotNull IScopes scopes) { return new TracingHeaders( new SentryTraceHeader( - propagationContext.getTraceId(), propagationContext.getSpanId(), null), + propagationContext.getTraceId(), + propagationContext.getSpanId(), + propagationContext.isSampled()), baggageHeader); } diff --git a/sentry/src/test/java/io/sentry/ScopesTest.kt b/sentry/src/test/java/io/sentry/ScopesTest.kt index a806ca2175e..f0e9833cb88 100644 --- a/sentry/src/test/java/io/sentry/ScopesTest.kt +++ b/sentry/src/test/java/io/sentry/ScopesTest.kt @@ -1968,6 +1968,23 @@ class ScopesTest { assertEquals(parentSpanId, transactionContext!!.parentSpanId) } + @Test + fun `continueTrace creates propagation context from headers and returns transaction context if performance enabled no sampled value`() { + val scopes = generateScopes() + val traceId = SentryId() + val parentSpanId = SpanId() + val transactionContext = scopes.continueTrace("$traceId-$parentSpanId", listOf("sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-trace_id=$traceId,sentry-transaction=HTTP%20GET")) + + scopes.configureScope { scope -> + assertEquals(traceId, scope.propagationContext.traceId) + assertEquals(parentSpanId, scope.propagationContext.parentSpanId) + } + + assertEquals(traceId, transactionContext!!.traceId) + assertEquals(parentSpanId, transactionContext!!.parentSpanId) + assertEquals(null, transactionContext!!.parentSamplingDecision) + } + @Test fun `continueTrace creates new propagation context if header invalid and returns transaction context if performance enabled`() { val scopes = generateScopes() diff --git a/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt b/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt index 62e2ba00552..096b0a81bd5 100644 --- a/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt +++ b/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt @@ -58,6 +58,7 @@ class TracingUtilsTest { assertNotNull(headers.baggageHeader) assertEquals(fixture.scope.propagationContext.spanId, headers.sentryTraceHeader.spanId) assertEquals(fixture.scope.propagationContext.traceId, headers.sentryTraceHeader.traceId) + assertEquals(fixture.scope.propagationContext.isSampled, headers.sentryTraceHeader.isSampled) assertTrue(headers.baggageHeader!!.value.contains("some-baggage-key=some-baggage-value")) assertTrue(headers.baggageHeader!!.value.contains("sentry-trace_id=${fixture.scope.propagationContext.traceId}")) assertFalse(fixture.scope.propagationContext.baggage!!.isMutable) @@ -73,6 +74,39 @@ class TracingUtilsTest { assertNotNull(headers.baggageHeader) assertEquals(fixture.scope.propagationContext.spanId, headers.sentryTraceHeader.spanId) assertEquals(fixture.scope.propagationContext.traceId, headers.sentryTraceHeader.traceId) + assertEquals(fixture.scope.propagationContext.isSampled, headers.sentryTraceHeader.isSampled) + assertTrue(headers.baggageHeader!!.value.contains("some-baggage-key=some-baggage-value")) + assertFalse(fixture.scope.propagationContext.baggage!!.isMutable) + } + + @Test + fun `returns headers if allowed from scope if span is noop sampled=true`() { + fixture.setup() + fixture.scope.propagationContext.isSampled = true + + val headers = TracingUtils.traceIfAllowed(fixture.scopes, "https://sentry.io/hello", fixture.preExistingBaggage, NoOpSpan.getInstance()) + + assertNotNull(headers) + assertNotNull(headers.baggageHeader) + assertEquals(fixture.scope.propagationContext.spanId, headers.sentryTraceHeader.spanId) + assertEquals(fixture.scope.propagationContext.traceId, headers.sentryTraceHeader.traceId) + assertEquals(fixture.scope.propagationContext.isSampled, headers.sentryTraceHeader.isSampled) + assertTrue(headers.baggageHeader!!.value.contains("some-baggage-key=some-baggage-value")) + assertFalse(fixture.scope.propagationContext.baggage!!.isMutable) + } + + @Test + fun `returns headers if allowed from scope if span is noop sampled=false`() { + fixture.setup() + fixture.scope.propagationContext.isSampled = false + + val headers = TracingUtils.traceIfAllowed(fixture.scopes, "https://sentry.io/hello", fixture.preExistingBaggage, NoOpSpan.getInstance()) + + assertNotNull(headers) + assertNotNull(headers.baggageHeader) + assertEquals(fixture.scope.propagationContext.spanId, headers.sentryTraceHeader.spanId) + assertEquals(fixture.scope.propagationContext.traceId, headers.sentryTraceHeader.traceId) + assertEquals(fixture.scope.propagationContext.isSampled, headers.sentryTraceHeader.isSampled) assertTrue(headers.baggageHeader!!.value.contains("some-baggage-key=some-baggage-value")) assertFalse(fixture.scope.propagationContext.baggage!!.isMutable) } From 156b915b2baddb4ac18ac1d4595fefb4ce786c43 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Mon, 2 Dec 2024 10:59:57 +0100 Subject: [PATCH 08/16] Use null sampled for TwP --- .../api/sentry-opentelemetry-core.api | 1 - .../opentelemetry/OtelSamplingUtil.java | 10 -------- .../OtelSentrySpanProcessor.java | 24 ++++++++++++++----- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api b/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api index 60e2010e6c5..fd2099a7303 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api +++ b/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api @@ -12,7 +12,6 @@ public final class io/sentry/opentelemetry/OtelInternalSpanDetectionUtil { public final class io/sentry/opentelemetry/OtelSamplingUtil { public fun ()V public static fun extractSamplingDecision (Lio/opentelemetry/api/common/Attributes;)Lio/sentry/TracesSamplingDecision; - public static fun extractSamplingDecisionOrDefault (Lio/opentelemetry/api/common/Attributes;)Lio/sentry/TracesSamplingDecision; } public final class io/sentry/opentelemetry/OtelSentryPropagator : io/opentelemetry/context/propagation/TextMapPropagator { diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSamplingUtil.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSamplingUtil.java index 45a8922741d..01f414f9022 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSamplingUtil.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSamplingUtil.java @@ -9,16 +9,6 @@ @ApiStatus.Internal public final class OtelSamplingUtil { - public static @NotNull TracesSamplingDecision extractSamplingDecisionOrDefault( - final @NotNull Attributes attributes) { - final @Nullable TracesSamplingDecision decision = extractSamplingDecision(attributes); - if (decision != null) { - return decision; - } else { - return new TracesSamplingDecision(false); - } - } - public static @Nullable TracesSamplingDecision extractSamplingDecision( final @NotNull Attributes attributes) { final @Nullable Boolean sampled = attributes.get(InternalSemanticAttributes.SAMPLED); diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSentrySpanProcessor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSentrySpanProcessor.java index 569067a856e..27149d821f3 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSentrySpanProcessor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSentrySpanProcessor.java @@ -49,9 +49,9 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri final @Nullable IOtelSpanWrapper sentryParentSpan = spanStorage.getSentrySpan(otelSpan.getParentSpanContext()); - @NotNull + @Nullable TracesSamplingDecision samplingDecision = - OtelSamplingUtil.extractSamplingDecisionOrDefault(otelSpan.toSpanData().getAttributes()); + OtelSamplingUtil.extractSamplingDecision(otelSpan.toSpanData().getAttributes()); @Nullable Baggage baggage = null; @Nullable SpanId sentryParentSpanId = null; otelSpan.setAttribute(IS_REMOTE_PARENT, otelSpan.getParentSpanContext().isRemote()); @@ -81,10 +81,7 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri } } - final boolean sampled = - samplingDecision != null - ? samplingDecision.getSampled() - : otelSpan.getSpanContext().isSampled(); + final @Nullable Boolean sampled = isSampled(otelSpan, samplingDecision); final @NotNull PropagationContext propagationContext = sentryTraceHeader == null @@ -111,6 +108,21 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri spanStorage.storeSentrySpan(spanContext, sentrySpan); } + private @Nullable Boolean isSampled( + final @NotNull ReadWriteSpan otelSpan, + final @Nullable TracesSamplingDecision samplingDecision) { + if (samplingDecision != null) { + return samplingDecision.getSampled(); + } + + if (otelSpan.getSpanContext().isSampled()) { + return true; + } + + // tracing without performance + return null; + } + private static void updatePropagationContext( IScopes scopes, PropagationContext propagationContext) { scopes.configureScope( From a90f3164e0641efff997ac99dd6277e500cc7336 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Mon, 2 Dec 2024 11:18:31 +0100 Subject: [PATCH 09/16] make todo url configurable --- .../samples/spring/boot/jakarta/TodoController.java | 12 ++++++++---- .../src/main/resources/application.properties | 1 + test/system-test-spring-server-start.sh | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/java/io/sentry/samples/spring/boot/jakarta/TodoController.java b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/java/io/sentry/samples/spring/boot/jakarta/TodoController.java index 8d86ddcb862..9648e47c45e 100644 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/java/io/sentry/samples/spring/boot/jakarta/TodoController.java +++ b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/java/io/sentry/samples/spring/boot/jakarta/TodoController.java @@ -7,6 +7,8 @@ import io.sentry.Sentry; import io.sentry.spring.jakarta.webflux.ReactorUtils; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; @@ -24,6 +26,9 @@ public class TodoController { private final RestClient restClient; private final Tracer tracer; + @Value("sentry.sample.todo-url") + public @Nullable String todoUrl; + public TodoController( RestTemplate restTemplate, WebClient webClient, RestClient restClient, Tracer tracer) { this.restTemplate = restTemplate; @@ -38,8 +43,7 @@ Todo todo(@PathVariable Long id) { try (final @NotNull Scope spanScope = otelSpan.makeCurrent()) { ISpan sentrySpan = Sentry.getSpan().startChild("todoSpanSentryApi"); try { - return restTemplate.getForObject( - "https://jsonplaceholder.typicode.com/todos/{id}", Todo.class, id); + return restTemplate.getForObject(todoUrl + "/todos/{id}", Todo.class, id); } finally { sentrySpan.finish(); } @@ -58,7 +62,7 @@ Todo todoWebClient(@PathVariable Long id) { x -> webClient .get() - .uri("https://jsonplaceholder.typicode.com/todos/{id}", id) + .uri(todoUrl + "/todos/{id}", id) .retrieve() .bodyToMono(Todo.class) .map(response -> response))) @@ -73,7 +77,7 @@ Todo todoRestClient(@PathVariable Long id) { try { return restClient .get() - .uri("https://jsonplaceholder.typicode.com/todos/{id}", id) + .uri(todoUrl + "/todos/{id}", id) .retrieve() .body(Todo.class); } finally { diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/resources/application.properties b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/resources/application.properties index b588f7979c2..06a18603aa5 100644 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/resources/application.properties +++ b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/resources/application.properties @@ -16,6 +16,7 @@ sentry.enable-backpressure-handling=true sentry.enable-spotlight=true sentry.enablePrettySerializationOutput=false in-app-includes="io.sentry.samples" +sentry.sample.todo-url=https://jsonplaceholder.typicode.com # Uncomment and set to true to enable aot compatibility # This flag disables all AOP related features (i.e. @SentryTransaction, @SentrySpan) diff --git a/test/system-test-spring-server-start.sh b/test/system-test-spring-server-start.sh index 1a6f3d5a069..b9ba9ed0b04 100755 --- a/test/system-test-spring-server-start.sh +++ b/test/system-test-spring-server-start.sh @@ -15,5 +15,5 @@ fi echo "$JAVA_AGENT_STRING" -SENTRY_DSN="http://502f25099c204a2fbf4cb16edc5975d1@localhost:8000/0" SENTRY_AUTO_INIT=${JAVA_AGENT_AUTO_INIT} SENTRY_TRACES_SAMPLE_RATE=1.0 OTEL_TRACES_EXPORTER=none OTEL_METRICS_EXPORTER=none OTEL_LOGS_EXPORTER=none java ${JAVA_AGENT_STRING} -jar sentry-samples/${SAMPLE_MODULE}/build/libs/${SAMPLE_MODULE}-0.0.1-SNAPSHOT.jar > spring-server.txt 2>&1 & +SENTRY_DSN="http://502f25099c204a2fbf4cb16edc5975d1@localhost:8000/0" SENTRY_SAMPLE_TODO_URL="http://localhost:48080" SENTRY_AUTO_INIT=${JAVA_AGENT_AUTO_INIT} SENTRY_TRACES_SAMPLE_RATE=1.0 OTEL_TRACES_EXPORTER=none OTEL_METRICS_EXPORTER=none OTEL_LOGS_EXPORTER=none java ${JAVA_AGENT_STRING} -jar sentry-samples/${SAMPLE_MODULE}/build/libs/${SAMPLE_MODULE}-0.0.1-SNAPSHOT.jar > spring-server.txt 2>&1 & echo $! > spring-server.pid From 20bd534754334a8a12b2e7c164ce48d4e6767b4a Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Mon, 2 Dec 2024 14:45:43 +0100 Subject: [PATCH 10/16] Build PropagationContext from sampling decision in OTel instead of incoming headers --- .../io/sentry/opentelemetry/OtelSentrySpanProcessor.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSentrySpanProcessor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSentrySpanProcessor.java index 27149d821f3..521bc9020c0 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSentrySpanProcessor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSentrySpanProcessor.java @@ -84,10 +84,8 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri final @Nullable Boolean sampled = isSampled(otelSpan, samplingDecision); final @NotNull PropagationContext propagationContext = - sentryTraceHeader == null - ? new PropagationContext( - new SentryId(traceId), sentrySpanId, sentryParentSpanId, baggage, sampled) - : PropagationContext.fromHeaders(sentryTraceHeader, baggage, sentrySpanId); + new PropagationContext( + new SentryId(traceId), sentrySpanId, sentryParentSpanId, baggage, sampled); updatePropagationContext(scopes, propagationContext); } From 4bb2032fa0526114a3b0b33beab6b2bcd640674c Mon Sep 17 00:00:00 2001 From: Sentry Github Bot Date: Mon, 2 Dec 2024 14:27:13 +0000 Subject: [PATCH 11/16] Format code --- .../sentry/samples/spring/boot/jakarta/TodoController.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/java/io/sentry/samples/spring/boot/jakarta/TodoController.java b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/java/io/sentry/samples/spring/boot/jakarta/TodoController.java index 9648e47c45e..09c9eab40e6 100644 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/java/io/sentry/samples/spring/boot/jakarta/TodoController.java +++ b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/java/io/sentry/samples/spring/boot/jakarta/TodoController.java @@ -75,11 +75,7 @@ Todo todoRestClient(@PathVariable Long id) { try (final @NotNull Scope spanScope = span.makeCurrent()) { ISpan sentrySpan = Sentry.getSpan().startChild("todoRestClientSpanSentryApi"); try { - return restClient - .get() - .uri(todoUrl + "/todos/{id}", id) - .retrieve() - .body(Todo.class); + return restClient.get().uri(todoUrl + "/todos/{id}", id).retrieve().body(Todo.class); } finally { sentrySpan.finish(); } From 74ace9cf1145e6b0291938f7755f60663aa10ccc Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Mon, 2 Dec 2024 15:29:02 +0100 Subject: [PATCH 12/16] changelog --- CHANGELOG.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 85cf84e17df..86d91f82834 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - When generating `sentry-trace` header from `PropagationContext` we now copy the `sampled` flag. - 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)) ## 8.0.0-rc.1 @@ -26,10 +27,6 @@ - Fix testTag not working for Jetpack Compose user interaction tracking ([#3878](https://github.com/getsentry/sentry-java/pull/3878)) -### Dependencies - -- Bump OpenTelemetry to 1.44.1, OpenTelemetry Java Agent to 2.10.0 and Semantic Conventions to 1.28.0 ([#3935](https://github.com/getsentry/sentry-java/pull/3935)) - ## 8.0.0-beta.3 ### Features From ae7c69e7f9f86ed92a5f3c4eaab09939bd9a7bbc Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Mon, 2 Dec 2024 15:30:24 +0100 Subject: [PATCH 13/16] Revert "make todo url configurable" This reverts commit a90f3164e0641efff997ac99dd6277e500cc7336. --- .../spring/boot/jakarta/TodoController.java | 16 ++++++++-------- .../src/main/resources/application.properties | 1 - test/system-test-spring-server-start.sh | 2 +- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/java/io/sentry/samples/spring/boot/jakarta/TodoController.java b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/java/io/sentry/samples/spring/boot/jakarta/TodoController.java index 09c9eab40e6..8d86ddcb862 100644 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/java/io/sentry/samples/spring/boot/jakarta/TodoController.java +++ b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/java/io/sentry/samples/spring/boot/jakarta/TodoController.java @@ -7,8 +7,6 @@ import io.sentry.Sentry; import io.sentry.spring.jakarta.webflux.ReactorUtils; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; @@ -26,9 +24,6 @@ public class TodoController { private final RestClient restClient; private final Tracer tracer; - @Value("sentry.sample.todo-url") - public @Nullable String todoUrl; - public TodoController( RestTemplate restTemplate, WebClient webClient, RestClient restClient, Tracer tracer) { this.restTemplate = restTemplate; @@ -43,7 +38,8 @@ Todo todo(@PathVariable Long id) { try (final @NotNull Scope spanScope = otelSpan.makeCurrent()) { ISpan sentrySpan = Sentry.getSpan().startChild("todoSpanSentryApi"); try { - return restTemplate.getForObject(todoUrl + "/todos/{id}", Todo.class, id); + return restTemplate.getForObject( + "https://jsonplaceholder.typicode.com/todos/{id}", Todo.class, id); } finally { sentrySpan.finish(); } @@ -62,7 +58,7 @@ Todo todoWebClient(@PathVariable Long id) { x -> webClient .get() - .uri(todoUrl + "/todos/{id}", id) + .uri("https://jsonplaceholder.typicode.com/todos/{id}", id) .retrieve() .bodyToMono(Todo.class) .map(response -> response))) @@ -75,7 +71,11 @@ Todo todoRestClient(@PathVariable Long id) { try (final @NotNull Scope spanScope = span.makeCurrent()) { ISpan sentrySpan = Sentry.getSpan().startChild("todoRestClientSpanSentryApi"); try { - return restClient.get().uri(todoUrl + "/todos/{id}", id).retrieve().body(Todo.class); + return restClient + .get() + .uri("https://jsonplaceholder.typicode.com/todos/{id}", id) + .retrieve() + .body(Todo.class); } finally { sentrySpan.finish(); } diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/resources/application.properties b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/resources/application.properties index 06a18603aa5..b588f7979c2 100644 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/resources/application.properties +++ b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/resources/application.properties @@ -16,7 +16,6 @@ sentry.enable-backpressure-handling=true sentry.enable-spotlight=true sentry.enablePrettySerializationOutput=false in-app-includes="io.sentry.samples" -sentry.sample.todo-url=https://jsonplaceholder.typicode.com # Uncomment and set to true to enable aot compatibility # This flag disables all AOP related features (i.e. @SentryTransaction, @SentrySpan) diff --git a/test/system-test-spring-server-start.sh b/test/system-test-spring-server-start.sh index b9ba9ed0b04..1a6f3d5a069 100755 --- a/test/system-test-spring-server-start.sh +++ b/test/system-test-spring-server-start.sh @@ -15,5 +15,5 @@ fi echo "$JAVA_AGENT_STRING" -SENTRY_DSN="http://502f25099c204a2fbf4cb16edc5975d1@localhost:8000/0" SENTRY_SAMPLE_TODO_URL="http://localhost:48080" SENTRY_AUTO_INIT=${JAVA_AGENT_AUTO_INIT} SENTRY_TRACES_SAMPLE_RATE=1.0 OTEL_TRACES_EXPORTER=none OTEL_METRICS_EXPORTER=none OTEL_LOGS_EXPORTER=none java ${JAVA_AGENT_STRING} -jar sentry-samples/${SAMPLE_MODULE}/build/libs/${SAMPLE_MODULE}-0.0.1-SNAPSHOT.jar > spring-server.txt 2>&1 & +SENTRY_DSN="http://502f25099c204a2fbf4cb16edc5975d1@localhost:8000/0" SENTRY_AUTO_INIT=${JAVA_AGENT_AUTO_INIT} SENTRY_TRACES_SAMPLE_RATE=1.0 OTEL_TRACES_EXPORTER=none OTEL_METRICS_EXPORTER=none OTEL_LOGS_EXPORTER=none java ${JAVA_AGENT_STRING} -jar sentry-samples/${SAMPLE_MODULE}/build/libs/${SAMPLE_MODULE}-0.0.1-SNAPSHOT.jar > spring-server.txt 2>&1 & echo $! > spring-server.pid From e29772c233f80db98c76eaed707e085727b7c573 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 3 Dec 2024 11:50:25 +0100 Subject: [PATCH 14/16] disable continueTrace and injecting tracing headers for ignored span origins --- .../sentry/openfeign/SentryFeignClient.java | 9 ++++++ .../sentry/openfeign/SentryFeignClientTest.kt | 11 ++++++++ .../SentrySpanRestClientCustomizerTest.kt | 19 +++++++++++++ .../SentrySpanRestTemplateCustomizerTest.kt | 15 ++++++++++ .../SentrySpanWebClientCustomizerTest.kt | 28 +++++++++++++++---- .../SentrySpanRestTemplateCustomizerTest.kt | 15 ++++++++++ .../boot/SentrySpanWebClientCustomizerTest.kt | 28 +++++++++++++++---- ...entrySpanClientHttpRequestInterceptor.java | 9 ++++++ .../SentrySpanClientWebRequestFilter.java | 9 ++++++ .../jakarta/tracing/SentryTracingFilter.java | 7 ++++- .../webflux/AbstractSentryWebFilter.java | 7 ++++- .../tracing/SentryTracingFilterTest.kt | 25 ++++++++++++++++- .../webflux/SentryWebFluxTracingFilterTest.kt | 27 +++++++++++++++++- ...entrySpanClientHttpRequestInterceptor.java | 9 ++++++ .../SentrySpanClientWebRequestFilter.java | 9 ++++++ .../spring/tracing/SentryTracingFilter.java | 7 ++++- .../spring/webflux/SentryWebFilter.java | 7 ++++- .../spring/tracing/SentryTracingFilterTest.kt | 25 ++++++++++++++++- .../webflux/SentryWebFluxTracingFilterTest.kt | 27 +++++++++++++++++- 19 files changed, 275 insertions(+), 18 deletions(-) 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()) + } + } } From 424b72ac1cb4ebaf590b433d401cf1c96228fadf Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 3 Dec 2024 14:16:16 +0100 Subject: [PATCH 15/16] also change apollo and okhttp --- .../apollo3/SentryApollo3HttpInterceptor.kt | 17 +++++++---- .../apollo3/SentryApollo3InterceptorTest.kt | 10 +++++++ .../sentry/apollo/SentryApolloInterceptor.kt | 7 ++++- .../apollo/SentryApolloInterceptorTest.kt | 10 +++++++ .../sentry/okhttp/SentryOkHttpInterceptor.kt | 30 ++++++++++++------- .../okhttp/SentryOkHttpInterceptorTest.kt | 26 ++++++++++++---- 6 files changed, 78 insertions(+), 22 deletions(-) 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) From 96f464722b102c89b5ae27ab39f525327c471fbb Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 3 Dec 2024 14:21:56 +0100 Subject: [PATCH 16/16] changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) 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