From 0c8d0f3df92cd53c3509da28ae906692ae7eaf62 Mon Sep 17 00:00:00 2001 From: markushi Date: Thu, 28 Aug 2025 10:53:57 +0200 Subject: [PATCH 01/11] Add support for w3c traceparent header --- sentry/api/sentry.api | 14 ++++ .../main/java/io/sentry/SentryOptions.java | 21 ++++++ .../java/io/sentry/W3CTraceparentHeader.java | 44 +++++++++++++ .../java/io/sentry/util/TracingUtils.java | 47 ++++++++++++-- .../test/java/io/sentry/SentryOptionsTest.kt | 13 ++++ .../io/sentry/W3CTraceparentHeaderTest.kt | 65 +++++++++++++++++++ .../java/io/sentry/util/TracingUtilsTest.kt | 62 ++++++++++++++++++ 7 files changed, 261 insertions(+), 5 deletions(-) create mode 100644 sentry/src/main/java/io/sentry/W3CTraceparentHeader.java create mode 100644 sentry/src/test/java/io/sentry/W3CTraceparentHeaderTest.kt diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 85fdd34a503..3fe737be8df 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -3405,6 +3405,7 @@ public class io/sentry/SentryOptions { public fun isGlobalHubMode ()Ljava/lang/Boolean; public fun isPrintUncaughtStackTrace ()Z public fun isProfilingEnabled ()Z + public fun isPropagateTraceparent ()Z public fun isSendClientReports ()Z public fun isSendDefaultPii ()Z public fun isSendModules ()Z @@ -3492,6 +3493,7 @@ public class io/sentry/SentryOptions { public fun setProfilesSampler (Lio/sentry/SentryOptions$ProfilesSamplerCallback;)V public fun setProfilingTracesHz (I)V public fun setProguardUuid (Ljava/lang/String;)V + public fun setPropagateTraceparent (Z)V public fun setProxy (Lio/sentry/SentryOptions$Proxy;)V public fun setReadTimeoutMillis (I)V public fun setRelease (Ljava/lang/String;)V @@ -4322,6 +4324,16 @@ public final class io/sentry/UserFeedback$JsonKeys { public fun ()V } +public final class io/sentry/W3CTraceparentHeader { + public static final field TRACEPARENT_HEADER Ljava/lang/String; + public fun (Lio/sentry/protocol/SentryId;Lio/sentry/SpanId;Ljava/lang/Boolean;)V + public fun getName ()Ljava/lang/String; + public fun getSpanId ()Lio/sentry/SpanId; + public fun getTraceId ()Lio/sentry/protocol/SentryId; + public fun getValue ()Ljava/lang/String; + public fun isSampled ()Ljava/lang/Boolean; +} + public final class io/sentry/backpressure/BackpressureMonitor : io/sentry/backpressure/IBackpressureMonitor, java/lang/Runnable { public fun (Lio/sentry/SentryOptions;Lio/sentry/IScopes;)V public fun close ()V @@ -7054,8 +7066,10 @@ public final class io/sentry/util/TracingUtils { public final class io/sentry/util/TracingUtils$TracingHeaders { public fun (Lio/sentry/SentryTraceHeader;Lio/sentry/BaggageHeader;)V + public fun (Lio/sentry/SentryTraceHeader;Lio/sentry/BaggageHeader;Lio/sentry/W3CTraceparentHeader;)V public fun getBaggageHeader ()Lio/sentry/BaggageHeader; public fun getSentryTraceHeader ()Lio/sentry/SentryTraceHeader; + public fun getW3cTraceparentHeader ()Lio/sentry/W3CTraceparentHeader; } public final class io/sentry/util/UUIDGenerator { diff --git a/sentry/src/main/java/io/sentry/SentryOptions.java b/sentry/src/main/java/io/sentry/SentryOptions.java index 419094067a5..0ac89481040 100644 --- a/sentry/src/main/java/io/sentry/SentryOptions.java +++ b/sentry/src/main/java/io/sentry/SentryOptions.java @@ -393,6 +393,9 @@ public class SentryOptions { private final @NotNull List defaultTracePropagationTargets = Collections.singletonList(DEFAULT_PROPAGATION_TARGETS); + /** Whether to propagate W3C traceparent HTTP header. */ + private boolean propagateTraceparent = false; + /** Proguard UUID. */ private @Nullable String proguardUuid; @@ -2110,6 +2113,24 @@ public void setTracePropagationTargets(final @Nullable List tracePropaga } } + /** + * Returns whether W3C traceparent HTTP header propagation is enabled. + * + * @return true if enabled false otherwise + */ + public boolean isPropagateTraceparent() { + return propagateTraceparent; + } + + /** + * Enables or disables W3C traceparent HTTP header propagation. + * + * @param propagateTraceparent true if enabled false otherwise + */ + public void setPropagateTraceparent(boolean propagateTraceparent) { + this.propagateTraceparent = propagateTraceparent; + } + /** * Returns a Proguard UUID. * diff --git a/sentry/src/main/java/io/sentry/W3CTraceparentHeader.java b/sentry/src/main/java/io/sentry/W3CTraceparentHeader.java new file mode 100644 index 00000000000..ff3eb0c55e5 --- /dev/null +++ b/sentry/src/main/java/io/sentry/W3CTraceparentHeader.java @@ -0,0 +1,44 @@ +package io.sentry; + +import io.sentry.protocol.SentryId; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** Represents W3C traceparent HTTP header. */ +public final class W3CTraceparentHeader { + public static final String TRACEPARENT_HEADER = "traceparent"; + + private final @NotNull SentryId traceId; + private final @NotNull SpanId spanId; + private final @Nullable Boolean sampled; + + public W3CTraceparentHeader( + final @NotNull SentryId traceId, + final @NotNull SpanId spanId, + final @Nullable Boolean sampled) { + this.traceId = traceId; + this.spanId = spanId; + this.sampled = sampled; + } + + public @NotNull String getName() { + return TRACEPARENT_HEADER; + } + + public @NotNull String getValue() { + String sampledFlag = sampled != null && sampled ? "01" : "00"; + return String.format("00-%s-%s-%s", traceId, spanId, sampledFlag); + } + + public @NotNull SentryId getTraceId() { + return traceId; + } + + public @NotNull SpanId getSpanId() { + return spanId; + } + + public @Nullable Boolean isSampled() { + return sampled; + } +} diff --git a/sentry/src/main/java/io/sentry/util/TracingUtils.java b/sentry/src/main/java/io/sentry/util/TracingUtils.java index 4af89a79035..673980e7359 100644 --- a/sentry/src/main/java/io/sentry/util/TracingUtils.java +++ b/sentry/src/main/java/io/sentry/util/TracingUtils.java @@ -10,7 +10,9 @@ import io.sentry.PropagationContext; import io.sentry.SentryOptions; import io.sentry.SentryTraceHeader; +import io.sentry.SpanContext; import io.sentry.TracesSamplingDecision; +import io.sentry.W3CTraceparentHeader; import java.util.List; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -59,8 +61,18 @@ public static void setTrace( final @NotNull SentryOptions sentryOptions = scopes.getOptions(); if (span != null && !span.isNoOp()) { - return new TracingHeaders( - span.toSentryTrace(), span.toBaggageHeader(thirdPartyBaggageHeaders)); + final @NotNull SentryTraceHeader sentryTraceHeader = span.toSentryTrace(); + final @Nullable BaggageHeader baggageHeader = span.toBaggageHeader(thirdPartyBaggageHeaders); + @Nullable W3CTraceparentHeader w3cTraceparentHeader = null; + + if (sentryOptions.isPropagateTraceparent()) { + final @NotNull SpanContext spanContext = span.getSpanContext(); + w3cTraceparentHeader = + new W3CTraceparentHeader( + spanContext.getTraceId(), spanContext.getSpanId(), sentryTraceHeader.isSampled()); + } + + return new TracingHeaders(sentryTraceHeader, baggageHeader, w3cTraceparentHeader); } else { final @NotNull PropagationContextHolder returnValue = new PropagationContextHolder(); scopes.configureScope( @@ -74,12 +86,22 @@ public static void setTrace( final @NotNull BaggageHeader baggageHeader = BaggageHeader.fromBaggageAndOutgoingHeader(baggage, thirdPartyBaggageHeaders); - return new TracingHeaders( + final @NotNull SentryTraceHeader sentryTraceHeader = new SentryTraceHeader( propagationContext.getTraceId(), propagationContext.getSpanId(), - propagationContext.isSampled()), - baggageHeader); + propagationContext.isSampled()); + + @Nullable W3CTraceparentHeader w3cTraceparentHeader = null; + if (sentryOptions.isPropagateTraceparent()) { + w3cTraceparentHeader = + new W3CTraceparentHeader( + propagationContext.getTraceId(), + propagationContext.getSpanId(), + propagationContext.isSampled()); + } + + return new TracingHeaders(sentryTraceHeader, baggageHeader, w3cTraceparentHeader); } return null; @@ -110,12 +132,23 @@ private static final class PropagationContextHolder { public static final class TracingHeaders { private final @NotNull SentryTraceHeader sentryTraceHeader; private final @Nullable BaggageHeader baggageHeader; + private final @Nullable W3CTraceparentHeader w3cTraceparentHeader; public TracingHeaders( final @NotNull SentryTraceHeader sentryTraceHeader, final @Nullable BaggageHeader baggageHeader) { this.sentryTraceHeader = sentryTraceHeader; this.baggageHeader = baggageHeader; + this.w3cTraceparentHeader = null; + } + + public TracingHeaders( + final @NotNull SentryTraceHeader sentryTraceHeader, + final @Nullable BaggageHeader baggageHeader, + final @Nullable W3CTraceparentHeader w3cTraceparentHeader) { + this.sentryTraceHeader = sentryTraceHeader; + this.baggageHeader = baggageHeader; + this.w3cTraceparentHeader = w3cTraceparentHeader; } public @NotNull SentryTraceHeader getSentryTraceHeader() { @@ -125,6 +158,10 @@ public TracingHeaders( public @Nullable BaggageHeader getBaggageHeader() { return baggageHeader; } + + public @Nullable W3CTraceparentHeader getW3cTraceparentHeader() { + return w3cTraceparentHeader; + } } /** Checks if a transaction is to be ignored. */ diff --git a/sentry/src/test/java/io/sentry/SentryOptionsTest.kt b/sentry/src/test/java/io/sentry/SentryOptionsTest.kt index 469b2f4b160..d7fef7a5798 100644 --- a/sentry/src/test/java/io/sentry/SentryOptionsTest.kt +++ b/sentry/src/test/java/io/sentry/SentryOptionsTest.kt @@ -858,4 +858,17 @@ class SentryOptionsTest { options.deadlineTimeout = -1L assertEquals(-1L, options.deadlineTimeout) } + + @Test + fun `propagateTraceparent option defaults to false`() { + val options = SentryOptions() + assertFalse(options.isPropagateTraceparent) + } + + @Test + fun `propagateTraceparent option can be changed`() { + val options = SentryOptions() + options.isPropagateTraceparent = true + assertTrue(options.isPropagateTraceparent) + } } diff --git a/sentry/src/test/java/io/sentry/W3CTraceparentHeaderTest.kt b/sentry/src/test/java/io/sentry/W3CTraceparentHeaderTest.kt new file mode 100644 index 00000000000..c2004adee70 --- /dev/null +++ b/sentry/src/test/java/io/sentry/W3CTraceparentHeaderTest.kt @@ -0,0 +1,65 @@ +package io.sentry + +import io.sentry.protocol.SentryId +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull +import kotlin.test.assertTrue + +class W3CTraceparentHeaderTest { + + @Test + fun `creates header with sampled true`() { + val traceId = SentryId("12345678123456781234567812345678") + val spanId = SpanId("1234567812345678") + val header = W3CTraceparentHeader(traceId, spanId, true) + + assertEquals("traceparent", header.name) + assertEquals("00-12345678123456781234567812345678-1234567812345678-01", header.value) + assertEquals(traceId, header.traceId) + assertEquals(spanId, header.spanId) + assertTrue(header.isSampled() ?: false) + } + + @Test + fun `creates header with sampled false`() { + val traceId = SentryId("12345678123456781234567812345678") + val spanId = SpanId("1234567812345678") + val header = W3CTraceparentHeader(traceId, spanId, false) + + assertEquals("traceparent", header.name) + assertEquals("00-12345678123456781234567812345678-1234567812345678-00", header.value) + assertEquals(traceId, header.traceId) + assertEquals(spanId, header.spanId) + assertEquals(false, header.isSampled()) + } + + @Test + fun `creates header with sampled null`() { + val traceId = SentryId("12345678123456781234567812345678") + val spanId = SpanId("1234567812345678") + val header = W3CTraceparentHeader(traceId, spanId, null) + + assertEquals("traceparent", header.name) + assertEquals("00-12345678123456781234567812345678-1234567812345678-00", header.value) + assertEquals(traceId, header.traceId) + assertEquals(spanId, header.spanId) + assertNull(header.isSampled()) + } + + @Test + fun `value follows W3C format`() { + val traceId = SentryId("abcdefabcdefabcdabcdefabcdefabcd") + val spanId = SpanId("abcdefabcdefabcd") + val header = W3CTraceparentHeader(traceId, spanId, true) + + val value = header.value + val parts = value.split("-") + + assertEquals(4, parts.size) + assertEquals("00", parts[0]) // Version + assertEquals("abcdefabcdefabcdabcdefabcdefabcd", parts[1]) // Trace ID (32 hex chars) + assertEquals("abcdefabcdefabcd", parts[2]) // Span ID (16 hex chars) + assertEquals("01", parts[3]) // Sampled flag + } +} diff --git a/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt b/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt index e97540ce142..8516fba8062 100644 --- a/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt +++ b/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt @@ -443,4 +443,66 @@ class TracingUtilsTest { assertTrue(sampleRand < 1.0) assertTrue(sampleRand >= 0.9999) } + + @Test + fun `trace does not return w3c traceparent header when propagateTraceparent is disabled`() { + val fixture = Fixture() + fixture.setup() + fixture.options.isPropagateTraceparent = false + + val tracingHeaders = TracingUtils.trace(fixture.scopes, null, fixture.span) + + assertNotNull(tracingHeaders) + assertNotNull(tracingHeaders.sentryTraceHeader) + assertNull(tracingHeaders.w3cTraceparentHeader) + } + + @Test + fun `trace returns w3c traceparent header when propagateTraceparent is enabled`() { + val fixture = Fixture() + fixture.setup() + fixture.options.isPropagateTraceparent = true + + val tracingHeaders = TracingUtils.trace(fixture.scopes, null, fixture.span) + + assertNotNull(tracingHeaders) + assertNotNull(tracingHeaders.sentryTraceHeader) + assertNotNull(tracingHeaders.w3cTraceparentHeader) + assertEquals("traceparent", tracingHeaders.w3cTraceparentHeader!!.name) + assertEquals(fixture.span.spanContext.traceId, tracingHeaders.w3cTraceparentHeader!!.traceId) + assertEquals(fixture.span.spanContext.spanId, tracingHeaders.w3cTraceparentHeader!!.spanId) + } + + @Test + fun `trace returns w3c traceparent header with correct sampling info`() { + val fixture = Fixture() + fixture.setup() + fixture.options.isPropagateTraceparent = true + + val tracingHeaders = TracingUtils.trace(fixture.scopes, null, fixture.span) + + assertNotNull(tracingHeaders) + val w3cHeader = tracingHeaders.w3cTraceparentHeader!! + assertEquals(fixture.span.toSentryTrace().isSampled(), w3cHeader.isSampled()) + } + + @Test + fun `trace returns w3c traceparent header when no span provided and propagateTraceparent is enabled`() { + val fixture = Fixture() + fixture.setup() + fixture.options.isPropagateTraceparent = true + + val tracingHeaders = TracingUtils.trace(fixture.scopes, null, null) + + assertNotNull(tracingHeaders) + assertNotNull(tracingHeaders.sentryTraceHeader) + assertNotNull(tracingHeaders.w3cTraceparentHeader) + + val sentryTrace = tracingHeaders.sentryTraceHeader + val w3cTrace = tracingHeaders.w3cTraceparentHeader!! + + assertEquals(sentryTrace.traceId, w3cTrace.traceId) + assertEquals(sentryTrace.spanId, w3cTrace.spanId) + assertEquals(sentryTrace.isSampled(), w3cTrace.isSampled()) + } } From 33b23dc587e6acef2c9171d820e895b07cdf361f Mon Sep 17 00:00:00 2001 From: markushi Date: Thu, 28 Aug 2025 10:55:37 +0200 Subject: [PATCH 02/11] Update Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5adcac058e3..7f62f97e75e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Add support for Spring Boot 4 and Spring 7 ([#4601](https://github.com/getsentry/sentry-java/pull/4601)) - NOTE: Our `sentry-opentelemetry-agentless-spring` is not working yet for Spring Boot 4. Please use `sentry-opentelemetry-agent` until OpenTelemetry has support for Spring Boot 4. - Replace `UUIDGenerator` implementation with Apache licensed code ([#4662](https://github.com/getsentry/sentry-java/pull/4662)) +- Add support for w3c traceparent header ([#4671](https://github.com/getsentry/sentry-java/pull/4671)) ## 8.20.0 From be3676bc2a074ce4181cece199434168565f4ad8 Mon Sep 17 00:00:00 2001 From: markushi Date: Thu, 28 Aug 2025 12:01:13 +0200 Subject: [PATCH 03/11] Add traceheader to integrattions, address feedback --- .../apollo3/SentryApollo3HttpInterceptor.kt | 3 ++ .../apollo4/SentryApollo4HttpInterceptor.kt | 3 ++ .../ktorClient/SentryKtorClientPlugin.kt | 3 ++ .../sentry/okhttp/SentryOkHttpInterceptor.kt | 3 ++ .../okhttp/SentryOkHttpInterceptorTest.kt | 36 +++++++++++++++++++ .../sentry/openfeign/SentryFeignClient.java | 6 ++++ ...entrySpanClientHttpRequestInterceptor.java | 6 ++++ ...entrySpanClientHttpRequestInterceptor.java | 6 ++++ ...entrySpanClientHttpRequestInterceptor.java | 6 ++++ sentry/api/sentry.api | 3 -- .../java/io/sentry/W3CTraceparentHeader.java | 13 +------ .../io/sentry/W3CTraceparentHeaderTest.kt | 26 ++++---------- .../java/io/sentry/util/TracingUtilsTest.kt | 34 +++++++++++++----- 13 files changed, 106 insertions(+), 42 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 9b3baca9a38..8337eeb7b15 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 @@ -132,6 +132,9 @@ constructor( .toMutableList() .apply { add(HttpHeader(baggageHeader.name, baggageHeader.value)) } } + it.w3cTraceparentHeader?.let { w3cHeader -> + cleanedHeaders.add(HttpHeader(w3cHeader.name, w3cHeader.value)) + } } } diff --git a/sentry-apollo-4/src/main/java/io/sentry/apollo4/SentryApollo4HttpInterceptor.kt b/sentry-apollo-4/src/main/java/io/sentry/apollo4/SentryApollo4HttpInterceptor.kt index 0b9a29f1e33..fcf50564e5a 100644 --- a/sentry-apollo-4/src/main/java/io/sentry/apollo4/SentryApollo4HttpInterceptor.kt +++ b/sentry-apollo-4/src/main/java/io/sentry/apollo4/SentryApollo4HttpInterceptor.kt @@ -132,6 +132,9 @@ constructor( .toMutableList() .apply { add(HttpHeader(baggageHeader.name, baggageHeader.value)) } } + it.w3cTraceparentHeader?.let { w3cHeader -> + cleanedHeaders.add(HttpHeader(w3cHeader.name, w3cHeader.value)) + } } } diff --git a/sentry-ktor-client/src/main/java/io/sentry/ktorClient/SentryKtorClientPlugin.kt b/sentry-ktor-client/src/main/java/io/sentry/ktorClient/SentryKtorClientPlugin.kt index a653304e992..44dd008b1f9 100644 --- a/sentry-ktor-client/src/main/java/io/sentry/ktorClient/SentryKtorClientPlugin.kt +++ b/sentry-ktor-client/src/main/java/io/sentry/ktorClient/SentryKtorClientPlugin.kt @@ -137,6 +137,9 @@ public val SentryKtorClientPlugin: ClientPlugin = request.headers.remove(BaggageHeader.BAGGAGE_HEADER) request.headers[it.name] = it.value } + tracingHeaders.w3cTraceparentHeader?.let { + request.headers[it.name] = it.value + } } } } 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 e36d3772bf3..9d4898391d9 100644 --- a/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpInterceptor.kt +++ b/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpInterceptor.kt @@ -116,6 +116,9 @@ public open class SentryOkHttpInterceptor( requestBuilder.removeHeader(BaggageHeader.BAGGAGE_HEADER) requestBuilder.addHeader(it.name, it.value) } + tracingHeaders.w3cTraceparentHeader?.let { + requestBuilder.addHeader(it.name, it.value) + } } } 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 5f3198c097c..175f18278ee 100644 --- a/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpInterceptorTest.kt +++ b/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpInterceptorTest.kt @@ -645,4 +645,40 @@ class SentryOkHttpInterceptorTest { val okHttpEvent = SentryOkHttpEventListener.eventMap[call]!! assertTrue(okHttpEvent.isEventFinished.get()) } + + @Test + fun `adds W3C traceparent header when propagateTraceparent is enabled`() { + val client = fixture.getSut( + optionsConfiguration = Sentry.OptionsConfiguration { it.isPropagateTraceparent = true } + ) + + fixture.server.enqueue(MockResponse().setResponseCode(200)) + + val request = getRequest("/test") + client.newCall(request).execute() + + val recordedRequest = fixture.server.takeRequest() + assertNotNull(recordedRequest.getHeader("sentry-trace")) + assertNotNull(recordedRequest.getHeader("traceparent")) + + val traceparent = recordedRequest.getHeader("traceparent")!! + assertTrue(traceparent.startsWith("00-")) + assertEquals(4, traceparent.split("-").size) + } + + @Test + fun `does not add W3C traceparent header when propagateTraceparent is disabled`() { + val client = fixture.getSut( + optionsConfiguration = Sentry.OptionsConfiguration { it.isPropagateTraceparent = false } + ) + + fixture.server.enqueue(MockResponse().setResponseCode(200)) + + val request = getRequest("/test") + client.newCall(request).execute() + + val recordedRequest = fixture.server.takeRequest() + assertNotNull(recordedRequest.getHeader("sentry-trace")) + assertNull(recordedRequest.getHeader("traceparent")) + } } 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 35b2b5c8fdb..c199a6805fc 100644 --- a/sentry-openfeign/src/main/java/io/sentry/openfeign/SentryFeignClient.java +++ b/sentry-openfeign/src/main/java/io/sentry/openfeign/SentryFeignClient.java @@ -17,6 +17,7 @@ import io.sentry.SpanDataConvention; import io.sentry.SpanOptions; import io.sentry.SpanStatus; +import io.sentry.W3CTraceparentHeader; import io.sentry.util.Objects; import io.sentry.util.SpanUtils; import io.sentry.util.TracingUtils; @@ -137,6 +138,11 @@ public Response execute(final @NotNull Request request, final @NotNull Request.O requestWrapper.removeHeader(BaggageHeader.BAGGAGE_HEADER); requestWrapper.header(baggageHeader.getName(), baggageHeader.getValue()); } + + final @Nullable W3CTraceparentHeader w3cTraceparentHeader = tracingHeaders.getW3cTraceparentHeader(); + if (w3cTraceparentHeader != null) { + requestWrapper.header(w3cTraceparentHeader.getName(), w3cTraceparentHeader.getValue()); + } } return requestWrapper.build(); diff --git a/sentry-spring-7/src/main/java/io/sentry/spring7/tracing/SentrySpanClientHttpRequestInterceptor.java b/sentry-spring-7/src/main/java/io/sentry/spring7/tracing/SentrySpanClientHttpRequestInterceptor.java index 901321f0213..776b328c1a8 100644 --- a/sentry-spring-7/src/main/java/io/sentry/spring7/tracing/SentrySpanClientHttpRequestInterceptor.java +++ b/sentry-spring-7/src/main/java/io/sentry/spring7/tracing/SentrySpanClientHttpRequestInterceptor.java @@ -6,6 +6,7 @@ import com.jakewharton.nopen.annotation.Open; import io.sentry.BaggageHeader; +import io.sentry.W3CTraceparentHeader; import io.sentry.Breadcrumb; import io.sentry.Hint; import io.sentry.IScopes; @@ -113,6 +114,11 @@ private void maybeAddTracingHeaders( if (baggageHeader != null) { request.getHeaders().set(baggageHeader.getName(), baggageHeader.getValue()); } + + final @Nullable W3CTraceparentHeader w3cTraceparentHeader = tracingHeaders.getW3cTraceparentHeader(); + if (w3cTraceparentHeader != null) { + request.getHeaders().add(w3cTraceparentHeader.getName(), w3cTraceparentHeader.getValue()); + } } } 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 f50c93976e5..bcbc642a788 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 @@ -6,6 +6,7 @@ import com.jakewharton.nopen.annotation.Open; import io.sentry.BaggageHeader; +import io.sentry.W3CTraceparentHeader; import io.sentry.Breadcrumb; import io.sentry.Hint; import io.sentry.IScopes; @@ -113,6 +114,11 @@ private void maybeAddTracingHeaders( if (baggageHeader != null) { request.getHeaders().set(baggageHeader.getName(), baggageHeader.getValue()); } + + final @Nullable W3CTraceparentHeader w3cTraceparentHeader = tracingHeaders.getW3cTraceparentHeader(); + if (w3cTraceparentHeader != null) { + request.getHeaders().add(w3cTraceparentHeader.getName(), w3cTraceparentHeader.getValue()); + } } } 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 17be912a2c6..e63373c3d2b 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 @@ -13,6 +13,7 @@ import io.sentry.SpanDataConvention; import io.sentry.SpanOptions; import io.sentry.SpanStatus; +import io.sentry.W3CTraceparentHeader; import io.sentry.util.Objects; import io.sentry.util.SpanUtils; import io.sentry.util.TracingUtils; @@ -105,6 +106,11 @@ private void maybeAddTracingHeaders( if (baggageHeader != null) { request.getHeaders().set(baggageHeader.getName(), baggageHeader.getValue()); } + + final @Nullable W3CTraceparentHeader w3cTraceparentHeader = tracingHeaders.getW3cTraceparentHeader(); + if (w3cTraceparentHeader != null) { + request.getHeaders().add(w3cTraceparentHeader.getName(), w3cTraceparentHeader.getValue()); + } } } diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 3fe737be8df..4a5a647a2ff 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -4328,10 +4328,7 @@ public final class io/sentry/W3CTraceparentHeader { public static final field TRACEPARENT_HEADER Ljava/lang/String; public fun (Lio/sentry/protocol/SentryId;Lio/sentry/SpanId;Ljava/lang/Boolean;)V public fun getName ()Ljava/lang/String; - public fun getSpanId ()Lio/sentry/SpanId; - public fun getTraceId ()Lio/sentry/protocol/SentryId; public fun getValue ()Ljava/lang/String; - public fun isSampled ()Ljava/lang/Boolean; } public final class io/sentry/backpressure/BackpressureMonitor : io/sentry/backpressure/IBackpressureMonitor, java/lang/Runnable { diff --git a/sentry/src/main/java/io/sentry/W3CTraceparentHeader.java b/sentry/src/main/java/io/sentry/W3CTraceparentHeader.java index ff3eb0c55e5..cf7a547914f 100644 --- a/sentry/src/main/java/io/sentry/W3CTraceparentHeader.java +++ b/sentry/src/main/java/io/sentry/W3CTraceparentHeader.java @@ -27,18 +27,7 @@ public W3CTraceparentHeader( public @NotNull String getValue() { String sampledFlag = sampled != null && sampled ? "01" : "00"; - return String.format("00-%s-%s-%s", traceId, spanId, sampledFlag); + return String.format("%s-%s-%s", traceId, spanId, sampledFlag); } - public @NotNull SentryId getTraceId() { - return traceId; - } - - public @NotNull SpanId getSpanId() { - return spanId; - } - - public @Nullable Boolean isSampled() { - return sampled; - } } diff --git a/sentry/src/test/java/io/sentry/W3CTraceparentHeaderTest.kt b/sentry/src/test/java/io/sentry/W3CTraceparentHeaderTest.kt index c2004adee70..b02a4d95d52 100644 --- a/sentry/src/test/java/io/sentry/W3CTraceparentHeaderTest.kt +++ b/sentry/src/test/java/io/sentry/W3CTraceparentHeaderTest.kt @@ -3,8 +3,6 @@ package io.sentry import io.sentry.protocol.SentryId import kotlin.test.Test import kotlin.test.assertEquals -import kotlin.test.assertNull -import kotlin.test.assertTrue class W3CTraceparentHeaderTest { @@ -15,10 +13,7 @@ class W3CTraceparentHeaderTest { val header = W3CTraceparentHeader(traceId, spanId, true) assertEquals("traceparent", header.name) - assertEquals("00-12345678123456781234567812345678-1234567812345678-01", header.value) - assertEquals(traceId, header.traceId) - assertEquals(spanId, header.spanId) - assertTrue(header.isSampled() ?: false) + assertEquals("12345678123456781234567812345678-1234567812345678-01", header.value) } @Test @@ -28,10 +23,7 @@ class W3CTraceparentHeaderTest { val header = W3CTraceparentHeader(traceId, spanId, false) assertEquals("traceparent", header.name) - assertEquals("00-12345678123456781234567812345678-1234567812345678-00", header.value) - assertEquals(traceId, header.traceId) - assertEquals(spanId, header.spanId) - assertEquals(false, header.isSampled()) + assertEquals("12345678123456781234567812345678-1234567812345678-00", header.value) } @Test @@ -41,10 +33,7 @@ class W3CTraceparentHeaderTest { val header = W3CTraceparentHeader(traceId, spanId, null) assertEquals("traceparent", header.name) - assertEquals("00-12345678123456781234567812345678-1234567812345678-00", header.value) - assertEquals(traceId, header.traceId) - assertEquals(spanId, header.spanId) - assertNull(header.isSampled()) + assertEquals("12345678123456781234567812345678-1234567812345678-00", header.value) } @Test @@ -56,10 +45,9 @@ class W3CTraceparentHeaderTest { val value = header.value val parts = value.split("-") - assertEquals(4, parts.size) - assertEquals("00", parts[0]) // Version - assertEquals("abcdefabcdefabcdabcdefabcdefabcd", parts[1]) // Trace ID (32 hex chars) - assertEquals("abcdefabcdefabcd", parts[2]) // Span ID (16 hex chars) - assertEquals("01", parts[3]) // Sampled flag + assertEquals(3, parts.size) + assertEquals("abcdefabcdefabcdabcdefabcdefabcd", parts[0]) // Trace ID (32 chars) + assertEquals("abcdefabcdefabcd", parts[1]) // Span ID (16 chars) + assertEquals("01", parts[2]) // Sampled flag (2 chars) } } diff --git a/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt b/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt index 8516fba8062..10b7fd9379c 100644 --- a/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt +++ b/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt @@ -469,8 +469,14 @@ class TracingUtilsTest { assertNotNull(tracingHeaders.sentryTraceHeader) assertNotNull(tracingHeaders.w3cTraceparentHeader) assertEquals("traceparent", tracingHeaders.w3cTraceparentHeader!!.name) - assertEquals(fixture.span.spanContext.traceId, tracingHeaders.w3cTraceparentHeader!!.traceId) - assertEquals(fixture.span.spanContext.spanId, tracingHeaders.w3cTraceparentHeader!!.spanId) + + val headerValue = tracingHeaders.w3cTraceparentHeader!!.value + assertTrue(headerValue.startsWith("00-")) + + val parts = headerValue.split("-") + assertEquals(4, parts.size) + assertEquals(fixture.span.spanContext.traceId.toString(), parts[1]) + assertEquals(fixture.span.spanContext.spanId.toString(), parts[2]) } @Test @@ -483,7 +489,17 @@ class TracingUtilsTest { assertNotNull(tracingHeaders) val w3cHeader = tracingHeaders.w3cTraceparentHeader!! - assertEquals(fixture.span.toSentryTrace().isSampled(), w3cHeader.isSampled()) + assertEquals("traceparent", w3cHeader.name) + + val headerValue = w3cHeader.value + assertTrue(headerValue.startsWith("00-")) + + val parts = headerValue.split("-") + assertEquals(4, parts.size) + + val sentryTrace = fixture.span.toSentryTrace() + val expectedFlag = if (sentryTrace.isSampled() == true) "01" else "00" + assertEquals(expectedFlag, parts[3]) } @Test @@ -498,11 +514,13 @@ class TracingUtilsTest { assertNotNull(tracingHeaders.sentryTraceHeader) assertNotNull(tracingHeaders.w3cTraceparentHeader) - val sentryTrace = tracingHeaders.sentryTraceHeader val w3cTrace = tracingHeaders.w3cTraceparentHeader!! - - assertEquals(sentryTrace.traceId, w3cTrace.traceId) - assertEquals(sentryTrace.spanId, w3cTrace.spanId) - assertEquals(sentryTrace.isSampled(), w3cTrace.isSampled()) + assertEquals("traceparent", w3cTrace.name) + + val headerValue = w3cTrace.value + assertTrue(headerValue.startsWith("00-")) + + val parts = headerValue.split("-") + assertEquals(4, parts.size) } } From 7233f476f5dbfbb97d7ff2210dffe8d65ffad110 Mon Sep 17 00:00:00 2001 From: Sentry Github Bot Date: Thu, 28 Aug 2025 10:04:23 +0000 Subject: [PATCH 04/11] Format code --- .../ktorClient/SentryKtorClientPlugin.kt | 4 +--- .../sentry/okhttp/SentryOkHttpInterceptor.kt | 4 +--- .../okhttp/SentryOkHttpInterceptorTest.kt | 20 ++++++++++--------- .../sentry/openfeign/SentryFeignClient.java | 3 ++- ...entrySpanClientHttpRequestInterceptor.java | 5 +++-- ...entrySpanClientHttpRequestInterceptor.java | 5 +++-- ...entrySpanClientHttpRequestInterceptor.java | 3 ++- .../java/io/sentry/W3CTraceparentHeader.java | 1 - .../java/io/sentry/util/TracingUtilsTest.kt | 14 ++++++------- 9 files changed, 30 insertions(+), 29 deletions(-) diff --git a/sentry-ktor-client/src/main/java/io/sentry/ktorClient/SentryKtorClientPlugin.kt b/sentry-ktor-client/src/main/java/io/sentry/ktorClient/SentryKtorClientPlugin.kt index 44dd008b1f9..2f559f804fc 100644 --- a/sentry-ktor-client/src/main/java/io/sentry/ktorClient/SentryKtorClientPlugin.kt +++ b/sentry-ktor-client/src/main/java/io/sentry/ktorClient/SentryKtorClientPlugin.kt @@ -137,9 +137,7 @@ public val SentryKtorClientPlugin: ClientPlugin = request.headers.remove(BaggageHeader.BAGGAGE_HEADER) request.headers[it.name] = it.value } - tracingHeaders.w3cTraceparentHeader?.let { - request.headers[it.name] = it.value - } + tracingHeaders.w3cTraceparentHeader?.let { request.headers[it.name] = it.value } } } } 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 9d4898391d9..be1ee1caf37 100644 --- a/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpInterceptor.kt +++ b/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpInterceptor.kt @@ -116,9 +116,7 @@ public open class SentryOkHttpInterceptor( requestBuilder.removeHeader(BaggageHeader.BAGGAGE_HEADER) requestBuilder.addHeader(it.name, it.value) } - tracingHeaders.w3cTraceparentHeader?.let { - requestBuilder.addHeader(it.name, it.value) - } + tracingHeaders.w3cTraceparentHeader?.let { requestBuilder.addHeader(it.name, it.value) } } } 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 175f18278ee..8b7667afc6d 100644 --- a/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpInterceptorTest.kt +++ b/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpInterceptorTest.kt @@ -648,10 +648,11 @@ class SentryOkHttpInterceptorTest { @Test fun `adds W3C traceparent header when propagateTraceparent is enabled`() { - val client = fixture.getSut( - optionsConfiguration = Sentry.OptionsConfiguration { it.isPropagateTraceparent = true } - ) - + val client = + fixture.getSut( + optionsConfiguration = Sentry.OptionsConfiguration { it.isPropagateTraceparent = true } + ) + fixture.server.enqueue(MockResponse().setResponseCode(200)) val request = getRequest("/test") @@ -660,7 +661,7 @@ class SentryOkHttpInterceptorTest { val recordedRequest = fixture.server.takeRequest() assertNotNull(recordedRequest.getHeader("sentry-trace")) assertNotNull(recordedRequest.getHeader("traceparent")) - + val traceparent = recordedRequest.getHeader("traceparent")!! assertTrue(traceparent.startsWith("00-")) assertEquals(4, traceparent.split("-").size) @@ -668,10 +669,11 @@ class SentryOkHttpInterceptorTest { @Test fun `does not add W3C traceparent header when propagateTraceparent is disabled`() { - val client = fixture.getSut( - optionsConfiguration = Sentry.OptionsConfiguration { it.isPropagateTraceparent = false } - ) - + val client = + fixture.getSut( + optionsConfiguration = Sentry.OptionsConfiguration { it.isPropagateTraceparent = false } + ) + fixture.server.enqueue(MockResponse().setResponseCode(200)) val request = getRequest("/test") 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 c199a6805fc..acd73bbec7c 100644 --- a/sentry-openfeign/src/main/java/io/sentry/openfeign/SentryFeignClient.java +++ b/sentry-openfeign/src/main/java/io/sentry/openfeign/SentryFeignClient.java @@ -139,7 +139,8 @@ public Response execute(final @NotNull Request request, final @NotNull Request.O requestWrapper.header(baggageHeader.getName(), baggageHeader.getValue()); } - final @Nullable W3CTraceparentHeader w3cTraceparentHeader = tracingHeaders.getW3cTraceparentHeader(); + final @Nullable W3CTraceparentHeader w3cTraceparentHeader = + tracingHeaders.getW3cTraceparentHeader(); if (w3cTraceparentHeader != null) { requestWrapper.header(w3cTraceparentHeader.getName(), w3cTraceparentHeader.getValue()); } diff --git a/sentry-spring-7/src/main/java/io/sentry/spring7/tracing/SentrySpanClientHttpRequestInterceptor.java b/sentry-spring-7/src/main/java/io/sentry/spring7/tracing/SentrySpanClientHttpRequestInterceptor.java index 776b328c1a8..50a8d0539b0 100644 --- a/sentry-spring-7/src/main/java/io/sentry/spring7/tracing/SentrySpanClientHttpRequestInterceptor.java +++ b/sentry-spring-7/src/main/java/io/sentry/spring7/tracing/SentrySpanClientHttpRequestInterceptor.java @@ -6,7 +6,6 @@ import com.jakewharton.nopen.annotation.Open; import io.sentry.BaggageHeader; -import io.sentry.W3CTraceparentHeader; import io.sentry.Breadcrumb; import io.sentry.Hint; import io.sentry.IScopes; @@ -14,6 +13,7 @@ import io.sentry.SpanDataConvention; import io.sentry.SpanOptions; import io.sentry.SpanStatus; +import io.sentry.W3CTraceparentHeader; import io.sentry.util.Objects; import io.sentry.util.SpanUtils; import io.sentry.util.TracingUtils; @@ -115,7 +115,8 @@ private void maybeAddTracingHeaders( request.getHeaders().set(baggageHeader.getName(), baggageHeader.getValue()); } - final @Nullable W3CTraceparentHeader w3cTraceparentHeader = tracingHeaders.getW3cTraceparentHeader(); + final @Nullable W3CTraceparentHeader w3cTraceparentHeader = + tracingHeaders.getW3cTraceparentHeader(); if (w3cTraceparentHeader != null) { request.getHeaders().add(w3cTraceparentHeader.getName(), w3cTraceparentHeader.getValue()); } 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 bcbc642a788..e305816bb05 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 @@ -6,7 +6,6 @@ import com.jakewharton.nopen.annotation.Open; import io.sentry.BaggageHeader; -import io.sentry.W3CTraceparentHeader; import io.sentry.Breadcrumb; import io.sentry.Hint; import io.sentry.IScopes; @@ -14,6 +13,7 @@ import io.sentry.SpanDataConvention; import io.sentry.SpanOptions; import io.sentry.SpanStatus; +import io.sentry.W3CTraceparentHeader; import io.sentry.util.Objects; import io.sentry.util.SpanUtils; import io.sentry.util.TracingUtils; @@ -115,7 +115,8 @@ private void maybeAddTracingHeaders( request.getHeaders().set(baggageHeader.getName(), baggageHeader.getValue()); } - final @Nullable W3CTraceparentHeader w3cTraceparentHeader = tracingHeaders.getW3cTraceparentHeader(); + final @Nullable W3CTraceparentHeader w3cTraceparentHeader = + tracingHeaders.getW3cTraceparentHeader(); if (w3cTraceparentHeader != null) { request.getHeaders().add(w3cTraceparentHeader.getName(), w3cTraceparentHeader.getValue()); } 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 e63373c3d2b..ed63c5ea080 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 @@ -107,7 +107,8 @@ private void maybeAddTracingHeaders( request.getHeaders().set(baggageHeader.getName(), baggageHeader.getValue()); } - final @Nullable W3CTraceparentHeader w3cTraceparentHeader = tracingHeaders.getW3cTraceparentHeader(); + final @Nullable W3CTraceparentHeader w3cTraceparentHeader = + tracingHeaders.getW3cTraceparentHeader(); if (w3cTraceparentHeader != null) { request.getHeaders().add(w3cTraceparentHeader.getName(), w3cTraceparentHeader.getValue()); } diff --git a/sentry/src/main/java/io/sentry/W3CTraceparentHeader.java b/sentry/src/main/java/io/sentry/W3CTraceparentHeader.java index cf7a547914f..6b22c640e81 100644 --- a/sentry/src/main/java/io/sentry/W3CTraceparentHeader.java +++ b/sentry/src/main/java/io/sentry/W3CTraceparentHeader.java @@ -29,5 +29,4 @@ public W3CTraceparentHeader( String sampledFlag = sampled != null && sampled ? "01" : "00"; return String.format("%s-%s-%s", traceId, spanId, sampledFlag); } - } diff --git a/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt b/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt index 10b7fd9379c..e3fd96b6ab7 100644 --- a/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt +++ b/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt @@ -469,10 +469,10 @@ class TracingUtilsTest { assertNotNull(tracingHeaders.sentryTraceHeader) assertNotNull(tracingHeaders.w3cTraceparentHeader) assertEquals("traceparent", tracingHeaders.w3cTraceparentHeader!!.name) - + val headerValue = tracingHeaders.w3cTraceparentHeader!!.value assertTrue(headerValue.startsWith("00-")) - + val parts = headerValue.split("-") assertEquals(4, parts.size) assertEquals(fixture.span.spanContext.traceId.toString(), parts[1]) @@ -490,13 +490,13 @@ class TracingUtilsTest { assertNotNull(tracingHeaders) val w3cHeader = tracingHeaders.w3cTraceparentHeader!! assertEquals("traceparent", w3cHeader.name) - + val headerValue = w3cHeader.value assertTrue(headerValue.startsWith("00-")) - + val parts = headerValue.split("-") assertEquals(4, parts.size) - + val sentryTrace = fixture.span.toSentryTrace() val expectedFlag = if (sentryTrace.isSampled() == true) "01" else "00" assertEquals(expectedFlag, parts[3]) @@ -516,10 +516,10 @@ class TracingUtilsTest { val w3cTrace = tracingHeaders.w3cTraceparentHeader!! assertEquals("traceparent", w3cTrace.name) - + val headerValue = w3cTrace.value assertTrue(headerValue.startsWith("00-")) - + val parts = headerValue.split("-") assertEquals(4, parts.size) } From 95d8f0cbf8340657fe65540a96622617797c9be8 Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Thu, 28 Aug 2025 12:06:38 +0200 Subject: [PATCH 05/11] Refine Changelog --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f62f97e75e..ab0a777769e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,14 @@ - NOTE: Our `sentry-opentelemetry-agentless-spring` is not working yet for Spring Boot 4. Please use `sentry-opentelemetry-agent` until OpenTelemetry has support for Spring Boot 4. - Replace `UUIDGenerator` implementation with Apache licensed code ([#4662](https://github.com/getsentry/sentry-java/pull/4662)) - Add support for w3c traceparent header ([#4671](https://github.com/getsentry/sentry-java/pull/4671)) + - This feature is disabled by default. If enabled, outgoing requests will include the w3c `traceparent` header. + - See https://develop.sentry.dev/sdk/telemetry/traces/distributed-tracing/#w3c-trace-context-header for more details. + ```kotlin + Sentry(Android).init(context) { options -> + // ... + options.isPropagateTraceparent = true + } + ``` ## 8.20.0 From 320eed7e2da534eec9eb8af5edd0fff044ef2717 Mon Sep 17 00:00:00 2001 From: markushi Date: Fri, 29 Aug 2025 07:01:49 +0200 Subject: [PATCH 06/11] Cleanup AI slop --- .../okhttp/SentryOkHttpInterceptorTest.kt | 4 -- .../main/java/io/sentry/SentryOptions.java | 2 +- .../java/io/sentry/W3CTraceparentHeader.java | 2 +- .../java/io/sentry/util/TracingUtilsTest.kt | 40 ++++--------------- 4 files changed, 9 insertions(+), 39 deletions(-) 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 8b7667afc6d..e2af5a18bb4 100644 --- a/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpInterceptorTest.kt +++ b/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpInterceptorTest.kt @@ -661,10 +661,6 @@ class SentryOkHttpInterceptorTest { val recordedRequest = fixture.server.takeRequest() assertNotNull(recordedRequest.getHeader("sentry-trace")) assertNotNull(recordedRequest.getHeader("traceparent")) - - val traceparent = recordedRequest.getHeader("traceparent")!! - assertTrue(traceparent.startsWith("00-")) - assertEquals(4, traceparent.split("-").size) } @Test diff --git a/sentry/src/main/java/io/sentry/SentryOptions.java b/sentry/src/main/java/io/sentry/SentryOptions.java index 0ac89481040..559f3d9f54d 100644 --- a/sentry/src/main/java/io/sentry/SentryOptions.java +++ b/sentry/src/main/java/io/sentry/SentryOptions.java @@ -2127,7 +2127,7 @@ public boolean isPropagateTraceparent() { * * @param propagateTraceparent true if enabled false otherwise */ - public void setPropagateTraceparent(boolean propagateTraceparent) { + public void setPropagateTraceparent(final boolean propagateTraceparent) { this.propagateTraceparent = propagateTraceparent; } diff --git a/sentry/src/main/java/io/sentry/W3CTraceparentHeader.java b/sentry/src/main/java/io/sentry/W3CTraceparentHeader.java index 6b22c640e81..696d2b584ce 100644 --- a/sentry/src/main/java/io/sentry/W3CTraceparentHeader.java +++ b/sentry/src/main/java/io/sentry/W3CTraceparentHeader.java @@ -26,7 +26,7 @@ public W3CTraceparentHeader( } public @NotNull String getValue() { - String sampledFlag = sampled != null && sampled ? "01" : "00"; + final String sampledFlag = sampled != null && sampled ? "01" : "00"; return String.format("%s-%s-%s", traceId, spanId, sampledFlag); } } diff --git a/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt b/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt index e3fd96b6ab7..8b5749d4c98 100644 --- a/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt +++ b/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt @@ -471,42 +471,17 @@ class TracingUtilsTest { assertEquals("traceparent", tracingHeaders.w3cTraceparentHeader!!.name) val headerValue = tracingHeaders.w3cTraceparentHeader!!.value - assertTrue(headerValue.startsWith("00-")) - val parts = headerValue.split("-") - assertEquals(4, parts.size) - assertEquals(fixture.span.spanContext.traceId.toString(), parts[1]) - assertEquals(fixture.span.spanContext.spanId.toString(), parts[2]) - } - - @Test - fun `trace returns w3c traceparent header with correct sampling info`() { - val fixture = Fixture() - fixture.setup() - fixture.options.isPropagateTraceparent = true - - val tracingHeaders = TracingUtils.trace(fixture.scopes, null, fixture.span) - - assertNotNull(tracingHeaders) - val w3cHeader = tracingHeaders.w3cTraceparentHeader!! - assertEquals("traceparent", w3cHeader.name) - - val headerValue = w3cHeader.value - assertTrue(headerValue.startsWith("00-")) - - val parts = headerValue.split("-") - assertEquals(4, parts.size) - - val sentryTrace = fixture.span.toSentryTrace() - val expectedFlag = if (sentryTrace.isSampled() == true) "01" else "00" - assertEquals(expectedFlag, parts[3]) + assertTrue(headerValue.contains(fixture.span.spanContext.traceId.toString())) + assertTrue(headerValue.contains(fixture.span.spanContext.spanId.toString())) + assertTrue(headerValue.endsWith("-01")) } @Test fun `trace returns w3c traceparent header when no span provided and propagateTraceparent is enabled`() { val fixture = Fixture() - fixture.setup() fixture.options.isPropagateTraceparent = true + fixture.setup() val tracingHeaders = TracingUtils.trace(fixture.scopes, null, null) @@ -518,9 +493,8 @@ class TracingUtilsTest { assertEquals("traceparent", w3cTrace.name) val headerValue = w3cTrace.value - assertTrue(headerValue.startsWith("00-")) - - val parts = headerValue.split("-") - assertEquals(4, parts.size) + assertTrue(headerValue.contains(fixture.scope.propagationContext.traceId.toString())) + assertTrue(headerValue.contains(fixture.scope.propagationContext.spanId.toString())) + assertTrue(headerValue.endsWith("-00")) } } From 9a94762ab5b166e9498a7298a35ff0ee60eb640a Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Fri, 29 Aug 2025 09:20:04 +0200 Subject: [PATCH 07/11] Add missing version to header value --- .../java/io/sentry/W3CTraceparentHeader.java | 2 +- .../io/sentry/W3CTraceparentHeaderTest.kt | 15 ++++++------ .../java/io/sentry/util/TracingUtilsTest.kt | 23 +++++++++++++------ 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/sentry/src/main/java/io/sentry/W3CTraceparentHeader.java b/sentry/src/main/java/io/sentry/W3CTraceparentHeader.java index 696d2b584ce..1eadc9881b6 100644 --- a/sentry/src/main/java/io/sentry/W3CTraceparentHeader.java +++ b/sentry/src/main/java/io/sentry/W3CTraceparentHeader.java @@ -27,6 +27,6 @@ public W3CTraceparentHeader( public @NotNull String getValue() { final String sampledFlag = sampled != null && sampled ? "01" : "00"; - return String.format("%s-%s-%s", traceId, spanId, sampledFlag); + return String.format("00-%s-%s-%s", traceId, spanId, sampledFlag); } } diff --git a/sentry/src/test/java/io/sentry/W3CTraceparentHeaderTest.kt b/sentry/src/test/java/io/sentry/W3CTraceparentHeaderTest.kt index b02a4d95d52..f9052779029 100644 --- a/sentry/src/test/java/io/sentry/W3CTraceparentHeaderTest.kt +++ b/sentry/src/test/java/io/sentry/W3CTraceparentHeaderTest.kt @@ -13,7 +13,7 @@ class W3CTraceparentHeaderTest { val header = W3CTraceparentHeader(traceId, spanId, true) assertEquals("traceparent", header.name) - assertEquals("12345678123456781234567812345678-1234567812345678-01", header.value) + assertEquals("00-12345678123456781234567812345678-1234567812345678-01", header.value) } @Test @@ -23,7 +23,7 @@ class W3CTraceparentHeaderTest { val header = W3CTraceparentHeader(traceId, spanId, false) assertEquals("traceparent", header.name) - assertEquals("12345678123456781234567812345678-1234567812345678-00", header.value) + assertEquals("00-12345678123456781234567812345678-1234567812345678-00", header.value) } @Test @@ -33,7 +33,7 @@ class W3CTraceparentHeaderTest { val header = W3CTraceparentHeader(traceId, spanId, null) assertEquals("traceparent", header.name) - assertEquals("12345678123456781234567812345678-1234567812345678-00", header.value) + assertEquals("00-12345678123456781234567812345678-1234567812345678-00", header.value) } @Test @@ -45,9 +45,10 @@ class W3CTraceparentHeaderTest { val value = header.value val parts = value.split("-") - assertEquals(3, parts.size) - assertEquals("abcdefabcdefabcdabcdefabcdefabcd", parts[0]) // Trace ID (32 chars) - assertEquals("abcdefabcdefabcd", parts[1]) // Span ID (16 chars) - assertEquals("01", parts[2]) // Sampled flag (2 chars) + assertEquals(4, parts.size) + assertEquals("00", parts[0]) // Version + assertEquals("abcdefabcdefabcdabcdefabcdefabcd", parts[1]) // Trace ID (32 hex chars) + assertEquals("abcdefabcdefabcd", parts[2]) // Span ID (16 hex chars) + assertEquals("01", parts[3]) // Sampled flag } } diff --git a/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt b/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt index 8b5749d4c98..5ffd1978b7d 100644 --- a/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt +++ b/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt @@ -471,10 +471,14 @@ class TracingUtilsTest { assertEquals("traceparent", tracingHeaders.w3cTraceparentHeader!!.name) val headerValue = tracingHeaders.w3cTraceparentHeader!!.value - - assertTrue(headerValue.contains(fixture.span.spanContext.traceId.toString())) - assertTrue(headerValue.contains(fixture.span.spanContext.spanId.toString())) - assertTrue(headerValue.endsWith("-01")) + assertTrue(headerValue.startsWith("00-")) + + val parts = headerValue.split("-") + assertEquals(4, parts.size) + assertEquals("00", parts[0]) + assertEquals(fixture.span.spanContext.traceId.toString(), parts[1]) + assertEquals(fixture.span.spanContext.spanId.toString(), parts[2]) + assertEquals("01", parts[3]) } @Test @@ -493,8 +497,13 @@ class TracingUtilsTest { assertEquals("traceparent", w3cTrace.name) val headerValue = w3cTrace.value - assertTrue(headerValue.contains(fixture.scope.propagationContext.traceId.toString())) - assertTrue(headerValue.contains(fixture.scope.propagationContext.spanId.toString())) - assertTrue(headerValue.endsWith("-00")) + assertTrue(headerValue.startsWith("00-")) + + val parts = headerValue.split("-") + assertEquals(4, parts.size) + assertEquals("00", parts[0]) + assertEquals(fixture.scope.propagationContext.traceId.toString(), parts[1]) + assertEquals(fixture.scope.propagationContext.spanId.toString(), parts[2]) + assertEquals("00", parts[3]) } } From 48b20080ed99b9800668bda2202f9d725855620a Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Mon, 1 Sep 2025 10:00:29 +0200 Subject: [PATCH 08/11] Add more tests --- .../apollo3/SentryApollo3InterceptorTest.kt | 21 ++++++++++++ .../SentryApollo4HttpInterceptorTest.kt | 21 ++++++++++++ .../ktorClient/SentryKtorClientPluginTest.kt | 26 +++++++++++++++ .../okhttp/SentryOkHttpInterceptorTest.kt | 9 ++--- .../sentry/openfeign/SentryFeignClientTest.kt | 27 +++++++++++++++ .../SentrySpanRestClientCustomizerTest.kt | 33 +++++++++++++++++++ .../SentrySpanRestTemplateCustomizerTest.kt | 25 ++++++++++++++ .../SentrySpanRestClientCustomizerTest.kt | 33 +++++++++++++++++++ .../SentrySpanRestTemplateCustomizerTest.kt | 25 ++++++++++++++ .../SentrySpanRestTemplateCustomizerTest.kt | 25 ++++++++++++++ .../java/io/sentry/util/TracingUtilsTest.kt | 8 +++-- 11 files changed, 247 insertions(+), 6 deletions(-) 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 375479f4d98..8316f6c0f33 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 @@ -23,6 +23,7 @@ import io.sentry.SpanStatus import io.sentry.TraceContext import io.sentry.TracesSamplingDecision import io.sentry.TransactionContext +import io.sentry.W3CTraceparentHeader import io.sentry.apollo3.SentryApollo3HttpInterceptor.BeforeSpanCallback import io.sentry.mockServerRequestTimeoutMillis import io.sentry.protocol.SdkVersion @@ -351,6 +352,26 @@ class SentryApollo3InterceptorTest { verify(fixture.scopes).span } + @Test + fun `adds W3C traceparent header when propagateTraceparent is enabled`() { + fixture.options.isPropagateTraceparent = true + executeQuery() + val recorderRequest = + fixture.server.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! + assertNotNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) + assertNotNull(recorderRequest.headers[W3CTraceparentHeader.TRACEPARENT_HEADER]) + } + + @Test + fun `does not add W3C traceparent header when propagateTraceparent is disabled`() { + fixture.options.isPropagateTraceparent = false + executeQuery() + val recorderRequest = + fixture.server.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! + assertNotNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) + assertNull(recorderRequest.headers[W3CTraceparentHeader.TRACEPARENT_HEADER]) + } + private fun assertTransactionDetails( it: SentryTransaction, httpStatusCode: Int? = 200, diff --git a/sentry-apollo-4/src/test/java/io/sentry/apollo4/SentryApollo4HttpInterceptorTest.kt b/sentry-apollo-4/src/test/java/io/sentry/apollo4/SentryApollo4HttpInterceptorTest.kt index a0bb7a2e154..d92cefe9772 100644 --- a/sentry-apollo-4/src/test/java/io/sentry/apollo4/SentryApollo4HttpInterceptorTest.kt +++ b/sentry-apollo-4/src/test/java/io/sentry/apollo4/SentryApollo4HttpInterceptorTest.kt @@ -26,6 +26,7 @@ import io.sentry.SpanStatus import io.sentry.TraceContext import io.sentry.TracesSamplingDecision import io.sentry.TransactionContext +import io.sentry.W3CTraceparentHeader import io.sentry.apollo4.SentryApollo4HttpInterceptor.BeforeSpanCallback import io.sentry.apollo4.generated.LaunchDetailsQuery import io.sentry.mockServerRequestTimeoutMillis @@ -363,6 +364,26 @@ abstract class SentryApollo4HttpInterceptorTest( verify(fixture.scopes).span } + @Test + fun `adds W3C traceparent header when propagateTraceparent is enabled`() { + fixture.options.isPropagateTraceparent = true + executeQuery() + val recorderRequest = + fixture.server.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! + assertNotNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) + assertNotNull(recorderRequest.headers[W3CTraceparentHeader.TRACEPARENT_HEADER]) + } + + @Test + fun `does not add W3C traceparent header when propagateTraceparent is disabled`() { + fixture.options.isPropagateTraceparent = false + executeQuery() + val recorderRequest = + fixture.server.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! + assertNotNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) + assertNull(recorderRequest.headers[W3CTraceparentHeader.TRACEPARENT_HEADER]) + } + private fun assertTransactionDetails( it: SentryTransaction, httpStatusCode: Int? = 200, diff --git a/sentry-ktor-client/src/test/java/io/sentry/ktorClient/SentryKtorClientPluginTest.kt b/sentry-ktor-client/src/test/java/io/sentry/ktorClient/SentryKtorClientPluginTest.kt index b15ae23d03d..976d3200e11 100644 --- a/sentry-ktor-client/src/test/java/io/sentry/ktorClient/SentryKtorClientPluginTest.kt +++ b/sentry-ktor-client/src/test/java/io/sentry/ktorClient/SentryKtorClientPluginTest.kt @@ -24,6 +24,7 @@ import io.sentry.SentryTracer import io.sentry.SpanDataConvention import io.sentry.SpanStatus import io.sentry.TransactionContext +import io.sentry.W3CTraceparentHeader import io.sentry.exception.SentryHttpClientException import io.sentry.mockServerRequestTimeoutMillis import java.util.concurrent.TimeUnit @@ -426,4 +427,29 @@ class SentryKtorClientPluginTest { assertTrue(baggageHeaderValues[0].contains("sentry-transaction=name")) assertTrue(baggageHeaderValues[0].contains("sentry-trace_id")) } + + @Test + fun `adds W3C traceparent header when propagateTraceparent is enabled`(): Unit = runBlocking { + val sut = + fixture.getSut(optionsConfiguration = { options -> options.isPropagateTraceparent = true }) + sut.get(fixture.server.url("/hello").toString()) + + val recordedRequest = + fixture.server.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! + assertNotNull(recordedRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) + assertNotNull(recordedRequest.headers[W3CTraceparentHeader.TRACEPARENT_HEADER]) + } + + @Test + fun `does not add W3C traceparent header when propagateTraceparent is disabled`(): Unit = + runBlocking { + val sut = + fixture.getSut(optionsConfiguration = { options -> options.isPropagateTraceparent = false }) + sut.get(fixture.server.url("/hello").toString()) + + val recordedRequest = + fixture.server.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! + assertNotNull(recordedRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) + assertNull(recordedRequest.headers[W3CTraceparentHeader.TRACEPARENT_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 e2af5a18bb4..bbdb3a86516 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.SpanDataConvention import io.sentry.SpanStatus import io.sentry.TransactionContext import io.sentry.TypeCheckHint +import io.sentry.W3CTraceparentHeader import io.sentry.exception.SentryHttpClientException import io.sentry.mockServerRequestTimeoutMillis import java.io.IOException @@ -659,8 +660,8 @@ class SentryOkHttpInterceptorTest { client.newCall(request).execute() val recordedRequest = fixture.server.takeRequest() - assertNotNull(recordedRequest.getHeader("sentry-trace")) - assertNotNull(recordedRequest.getHeader("traceparent")) + assertNotNull(recordedRequest.getHeader(SentryTraceHeader.SENTRY_TRACE_HEADER)) + assertNotNull(recordedRequest.getHeader(W3CTraceparentHeader.TRACEPARENT_HEADER)) } @Test @@ -676,7 +677,7 @@ class SentryOkHttpInterceptorTest { client.newCall(request).execute() val recordedRequest = fixture.server.takeRequest() - assertNotNull(recordedRequest.getHeader("sentry-trace")) - assertNull(recordedRequest.getHeader("traceparent")) + assertNotNull(recordedRequest.getHeader(SentryTraceHeader.SENTRY_TRACE_HEADER)) + assertNull(recordedRequest.getHeader(W3CTraceparentHeader.TRACEPARENT_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 66fbd86a91a..571a2339326 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.W3CTraceparentHeader import io.sentry.mockServerRequestTimeoutMillis import java.util.concurrent.TimeUnit import kotlin.test.BeforeTest @@ -316,6 +317,32 @@ class SentryFeignClientTest { assertNotNull(httpClientSpan.spanContext.sampled) { assertFalse(it) } } + @Test + fun `adds W3C traceparent header when propagateTraceparent is enabled`() { + fixture.sentryOptions.isTraceSampling = true + fixture.sentryOptions.isPropagateTraceparent = true + fixture.sentryOptions.dsn = "https://key@sentry.io/proj" + val sut = fixture.getSut() + sut.getOk() + val recorderRequest = + fixture.server.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! + assertNotNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) + assertNotNull(recorderRequest.headers[W3CTraceparentHeader.TRACEPARENT_HEADER]) + } + + @Test + fun `does not add W3C traceparent header when propagateTraceparent is disabled`() { + fixture.sentryOptions.isTraceSampling = true + fixture.sentryOptions.isPropagateTraceparent = false + fixture.sentryOptions.dsn = "https://key@sentry.io/proj" + val sut = fixture.getSut() + sut.getOk() + val recorderRequest = + fixture.server.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! + assertNotNull(recorderRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) + assertNull(recorderRequest.headers[W3CTraceparentHeader.TRACEPARENT_HEADER]) + } + interface MockApi { @RequestLine("GET /status/200") fun getOk(): String diff --git a/sentry-spring-boot-4/src/test/kotlin/io/sentry/spring/boot4/SentrySpanRestClientCustomizerTest.kt b/sentry-spring-boot-4/src/test/kotlin/io/sentry/spring/boot4/SentrySpanRestClientCustomizerTest.kt index fb803a3100f..8c19c7c7066 100644 --- a/sentry-spring-boot-4/src/test/kotlin/io/sentry/spring/boot4/SentrySpanRestClientCustomizerTest.kt +++ b/sentry-spring-boot-4/src/test/kotlin/io/sentry/spring/boot4/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.W3CTraceparentHeader import io.sentry.mockServerRequestTimeoutMillis import java.time.Duration import java.util.concurrent.TimeUnit @@ -383,4 +384,36 @@ class SentrySpanRestClientCustomizerTest { anyOrNull(), ) } + + @Test + fun `adds W3C traceparent header when propagateTraceparent is enabled`() { + fixture.sentryOptions.isPropagateTraceparent = true + fixture + .getSut(isTransactionActive = true, includeMockServerInTracingOrigins = true) + .build() + .get() + .uri(fixture.url) + .retrieve() + .toEntity(String::class.java) + val recordedRequest = + fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! + assertNotNull(recordedRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) + assertNotNull(recordedRequest.headers[W3CTraceparentHeader.TRACEPARENT_HEADER]) + } + + @Test + fun `does not add W3C traceparent header when propagateTraceparent is disabled`() { + fixture.sentryOptions.isPropagateTraceparent = false + fixture + .getSut(isTransactionActive = true, includeMockServerInTracingOrigins = true) + .build() + .get() + .uri(fixture.url) + .retrieve() + .toEntity(String::class.java) + val recordedRequest = + fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! + assertNotNull(recordedRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) + assertNull(recordedRequest.headers[W3CTraceparentHeader.TRACEPARENT_HEADER]) + } } diff --git a/sentry-spring-boot-4/src/test/kotlin/io/sentry/spring/boot4/SentrySpanRestTemplateCustomizerTest.kt b/sentry-spring-boot-4/src/test/kotlin/io/sentry/spring/boot4/SentrySpanRestTemplateCustomizerTest.kt index c6bd6d2ccaf..988824465bb 100644 --- a/sentry-spring-boot-4/src/test/kotlin/io/sentry/spring/boot4/SentrySpanRestTemplateCustomizerTest.kt +++ b/sentry-spring-boot-4/src/test/kotlin/io/sentry/spring/boot4/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.W3CTraceparentHeader import io.sentry.mockServerRequestTimeoutMillis import java.time.Duration import java.util.concurrent.TimeUnit @@ -328,4 +329,28 @@ class SentrySpanRestTemplateCustomizerTest { anyOrNull(), ) } + + @Test + fun `adds W3C traceparent header when propagateTraceparent is enabled`() { + fixture.sentryOptions.isPropagateTraceparent = true + fixture + .getSut(isTransactionActive = true, includeMockServerInTracingOrigins = true) + .getForObject(fixture.url, String::class.java) + val recordedRequest = + fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! + assertNotNull(recordedRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) + assertNotNull(recordedRequest.headers[W3CTraceparentHeader.TRACEPARENT_HEADER]) + } + + @Test + fun `does not add W3C traceparent header when propagateTraceparent is disabled`() { + fixture.sentryOptions.isPropagateTraceparent = false + fixture + .getSut(isTransactionActive = true, includeMockServerInTracingOrigins = true) + .getForObject(fixture.url, String::class.java) + val recordedRequest = + fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! + assertNotNull(recordedRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) + assertNull(recordedRequest.headers[W3CTraceparentHeader.TRACEPARENT_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 a0ce38e7c3d..7a2e6a0ff4f 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.W3CTraceparentHeader import io.sentry.mockServerRequestTimeoutMillis import java.time.Duration import java.util.concurrent.TimeUnit @@ -383,4 +384,36 @@ class SentrySpanRestClientCustomizerTest { anyOrNull(), ) } + + @Test + fun `adds W3C traceparent header when propagateTraceparent is enabled`() { + fixture.sentryOptions.isPropagateTraceparent = true + fixture + .getSut(isTransactionActive = true, includeMockServerInTracingOrigins = true) + .build() + .get() + .uri(fixture.url) + .retrieve() + .toEntity(String::class.java) + val recordedRequest = + fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! + assertNotNull(recordedRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) + assertNotNull(recordedRequest.headers[W3CTraceparentHeader.TRACEPARENT_HEADER]) + } + + @Test + fun `does not add W3C traceparent header when propagateTraceparent is disabled`() { + fixture.sentryOptions.isPropagateTraceparent = false + fixture + .getSut(isTransactionActive = true, includeMockServerInTracingOrigins = true) + .build() + .get() + .uri(fixture.url) + .retrieve() + .toEntity(String::class.java) + val recordedRequest = + fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! + assertNotNull(recordedRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) + assertNull(recordedRequest.headers[W3CTraceparentHeader.TRACEPARENT_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 f7a7f55c5db..c6c7707aab9 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.W3CTraceparentHeader import io.sentry.mockServerRequestTimeoutMillis import java.time.Duration import java.util.concurrent.TimeUnit @@ -328,4 +329,28 @@ class SentrySpanRestTemplateCustomizerTest { anyOrNull(), ) } + + @Test + fun `adds W3C traceparent header when propagateTraceparent is enabled`() { + fixture.sentryOptions.isPropagateTraceparent = true + fixture + .getSut(isTransactionActive = true, includeMockServerInTracingOrigins = true) + .getForObject(fixture.url, String::class.java) + val recordedRequest = + fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! + assertNotNull(recordedRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) + assertNotNull(recordedRequest.headers[W3CTraceparentHeader.TRACEPARENT_HEADER]) + } + + @Test + fun `does not add W3C traceparent header when propagateTraceparent is disabled`() { + fixture.sentryOptions.isPropagateTraceparent = false + fixture + .getSut(isTransactionActive = true, includeMockServerInTracingOrigins = true) + .getForObject(fixture.url, String::class.java) + val recordedRequest = + fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! + assertNotNull(recordedRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) + assertNull(recordedRequest.headers[W3CTraceparentHeader.TRACEPARENT_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 6bc1aade4e9..29699f61022 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.W3CTraceparentHeader import io.sentry.mockServerRequestTimeoutMillis import java.time.Duration import java.util.concurrent.TimeUnit @@ -328,4 +329,28 @@ class SentrySpanRestTemplateCustomizerTest { anyOrNull(), ) } + + @Test + fun `adds W3C traceparent header when propagateTraceparent is enabled`() { + fixture.sentryOptions.isPropagateTraceparent = true + fixture + .getSut(isTransactionActive = true, includeMockServerInTracingOrigins = true) + .getForObject(fixture.url, String::class.java) + val recordedRequest = + fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! + assertNotNull(recordedRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) + assertNotNull(recordedRequest.headers[W3CTraceparentHeader.TRACEPARENT_HEADER]) + } + + @Test + fun `does not add W3C traceparent header when propagateTraceparent is disabled`() { + fixture.sentryOptions.isPropagateTraceparent = false + fixture + .getSut(isTransactionActive = true, includeMockServerInTracingOrigins = true) + .getForObject(fixture.url, String::class.java) + val recordedRequest = + fixture.mockServer.takeRequest(mockServerRequestTimeoutMillis, TimeUnit.MILLISECONDS)!! + assertNotNull(recordedRequest.headers[SentryTraceHeader.SENTRY_TRACE_HEADER]) + assertNull(recordedRequest.headers[W3CTraceparentHeader.TRACEPARENT_HEADER]) + } } diff --git a/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt b/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt index 5ffd1978b7d..9c906712261 100644 --- a/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt +++ b/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt @@ -14,6 +14,7 @@ import io.sentry.SpanId import io.sentry.SpanOptions import io.sentry.TracesSamplingDecision import io.sentry.TransactionContext +import io.sentry.W3CTraceparentHeader import io.sentry.protocol.SentryId import kotlin.test.assertEquals import kotlin.test.assertFalse @@ -468,7 +469,10 @@ class TracingUtilsTest { assertNotNull(tracingHeaders) assertNotNull(tracingHeaders.sentryTraceHeader) assertNotNull(tracingHeaders.w3cTraceparentHeader) - assertEquals("traceparent", tracingHeaders.w3cTraceparentHeader!!.name) + assertEquals( + W3CTraceparentHeader.TRACEPARENT_HEADER, + tracingHeaders.w3cTraceparentHeader!!.name, + ) val headerValue = tracingHeaders.w3cTraceparentHeader!!.value assertTrue(headerValue.startsWith("00-")) @@ -494,7 +498,7 @@ class TracingUtilsTest { assertNotNull(tracingHeaders.w3cTraceparentHeader) val w3cTrace = tracingHeaders.w3cTraceparentHeader!! - assertEquals("traceparent", w3cTrace.name) + assertEquals(W3CTraceparentHeader.TRACEPARENT_HEADER, w3cTrace.name) val headerValue = w3cTrace.value assertTrue(headerValue.startsWith("00-")) From 238852f7ba5e2fa1da59d618bb56576468146fcc Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Mon, 1 Sep 2025 12:30:39 +0200 Subject: [PATCH 09/11] Add more tests --- ...trySpanClientHttpRequestInterceptorTest.kt | 78 +++++++++++++++++++ ...trySpanClientHttpRequestInterceptorTest.kt | 78 +++++++++++++++++++ ...trySpanClientHttpRequestInterceptorTest.kt | 78 +++++++++++++++++++ 3 files changed, 234 insertions(+) create mode 100644 sentry-spring-7/src/test/kotlin/io/sentry/spring7/tracing/SentrySpanClientHttpRequestInterceptorTest.kt create mode 100644 sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentrySpanClientHttpRequestInterceptorTest.kt create mode 100644 sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentrySpanClientHttpRequestInterceptorTest.kt diff --git a/sentry-spring-7/src/test/kotlin/io/sentry/spring7/tracing/SentrySpanClientHttpRequestInterceptorTest.kt b/sentry-spring-7/src/test/kotlin/io/sentry/spring7/tracing/SentrySpanClientHttpRequestInterceptorTest.kt new file mode 100644 index 00000000000..1680179592c --- /dev/null +++ b/sentry-spring-7/src/test/kotlin/io/sentry/spring7/tracing/SentrySpanClientHttpRequestInterceptorTest.kt @@ -0,0 +1,78 @@ +package io.sentry.spring7.tracing + +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.W3CTraceparentHeader +import java.net.URI +import kotlin.test.Test +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import org.junit.runner.RunWith +import org.mockito.kotlin.any +import org.mockito.kotlin.doAnswer +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever +import org.springframework.http.HttpMethod +import org.springframework.http.client.ClientHttpRequestExecution +import org.springframework.http.client.ClientHttpResponse +import org.springframework.mock.http.client.MockClientHttpRequest +import org.springframework.test.context.junit4.SpringRunner + +@RunWith(SpringRunner::class) +class SentrySpanClientHttpRequestInterceptorTest { + + class Fixture { + val request = MockClientHttpRequest(HttpMethod.GET, URI.create("https://example.com/users/123")) + + val options = + SentryOptions().apply { + dsn = "https://key@sentry.io/proj" + tracesSampleRate = 1.0 + } + val scope = Scope(options) + + val scopes = mock() + val requestExecution = mock() + val body = "data".toByteArray() + + init { + whenever(scopes.options).thenReturn(options) + doAnswer { (it.arguments[0] as ScopeCallback).run(scope) } + .whenever(scopes) + .configureScope(any()) + + whenever(requestExecution.execute(any(), any())).thenReturn(mock()) + } + + fun create( + config: Sentry.OptionsConfiguration + ): SentrySpanClientHttpRequestInterceptor { + config.configure(options) + return SentrySpanClientHttpRequestInterceptor(scopes) + } + } + + val fixture = Fixture() + + @Test + fun `attaches w3c trace parent header when enabled`() { + val sut = fixture.create { options -> options.isPropagateTraceparent = true } + sut.intercept(fixture.request, fixture.body, fixture.requestExecution) + + assertNotNull(fixture.request.headers.get(SentryTraceHeader.SENTRY_TRACE_HEADER)) + assertNotNull(fixture.request.headers.get(W3CTraceparentHeader.TRACEPARENT_HEADER)) + } + + @Test + fun `does not attach w3c trace parent header when enabled`() { + val sut = fixture.create { options -> options.isPropagateTraceparent = false } + sut.intercept(fixture.request, fixture.body, fixture.requestExecution) + + assertNotNull(fixture.request.headers.get(SentryTraceHeader.SENTRY_TRACE_HEADER)) + assertNull(fixture.request.headers.get(W3CTraceparentHeader.TRACEPARENT_HEADER)) + } +} diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentrySpanClientHttpRequestInterceptorTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentrySpanClientHttpRequestInterceptorTest.kt new file mode 100644 index 00000000000..05b77f3d837 --- /dev/null +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentrySpanClientHttpRequestInterceptorTest.kt @@ -0,0 +1,78 @@ +package io.sentry.spring.jakarta.tracing + +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.W3CTraceparentHeader +import java.net.URI +import kotlin.test.Test +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import org.junit.runner.RunWith +import org.mockito.kotlin.any +import org.mockito.kotlin.doAnswer +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever +import org.springframework.http.HttpMethod +import org.springframework.http.client.ClientHttpRequestExecution +import org.springframework.http.client.ClientHttpResponse +import org.springframework.mock.http.client.MockClientHttpRequest +import org.springframework.test.context.junit4.SpringRunner + +@RunWith(SpringRunner::class) +class SentrySpanClientHttpRequestInterceptorTest { + + class Fixture { + val request = MockClientHttpRequest(HttpMethod.GET, URI.create("https://example.com/users/123")) + + val options = + SentryOptions().apply { + dsn = "https://key@sentry.io/proj" + tracesSampleRate = 1.0 + } + val scope = Scope(options) + + val scopes = mock() + val requestExecution = mock() + val body = "data".toByteArray() + + init { + whenever(scopes.options).thenReturn(options) + doAnswer { (it.arguments[0] as ScopeCallback).run(scope) } + .whenever(scopes) + .configureScope(any()) + + whenever(requestExecution.execute(any(), any())).thenReturn(mock()) + } + + fun create( + config: Sentry.OptionsConfiguration + ): SentrySpanClientHttpRequestInterceptor { + config.configure(options) + return SentrySpanClientHttpRequestInterceptor(scopes) + } + } + + val fixture = Fixture() + + @Test + fun `attaches w3c trace parent header when enabled`() { + val sut = fixture.create { options -> options.isPropagateTraceparent = true } + sut.intercept(fixture.request, fixture.body, fixture.requestExecution) + + assertNotNull(fixture.request.headers.get(SentryTraceHeader.SENTRY_TRACE_HEADER)) + assertNotNull(fixture.request.headers.get(W3CTraceparentHeader.TRACEPARENT_HEADER)) + } + + @Test + fun `does not attach w3c trace parent header when enabled`() { + val sut = fixture.create { options -> options.isPropagateTraceparent = false } + sut.intercept(fixture.request, fixture.body, fixture.requestExecution) + + assertNotNull(fixture.request.headers.get(SentryTraceHeader.SENTRY_TRACE_HEADER)) + assertNull(fixture.request.headers.get(W3CTraceparentHeader.TRACEPARENT_HEADER)) + } +} diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentrySpanClientHttpRequestInterceptorTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentrySpanClientHttpRequestInterceptorTest.kt new file mode 100644 index 00000000000..665c6fdbb9d --- /dev/null +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentrySpanClientHttpRequestInterceptorTest.kt @@ -0,0 +1,78 @@ +package io.sentry.spring.tracing + +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.W3CTraceparentHeader +import java.net.URI +import kotlin.test.Test +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import org.junit.runner.RunWith +import org.mockito.kotlin.any +import org.mockito.kotlin.doAnswer +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever +import org.springframework.http.HttpMethod +import org.springframework.http.client.ClientHttpRequestExecution +import org.springframework.http.client.ClientHttpResponse +import org.springframework.mock.http.client.MockClientHttpRequest +import org.springframework.test.context.junit4.SpringRunner + +@RunWith(SpringRunner::class) +class SentrySpanClientHttpRequestInterceptorTest { + + class Fixture { + val request = MockClientHttpRequest(HttpMethod.GET, URI.create("https://example.com/users/123")) + + val options = + SentryOptions().apply { + dsn = "https://key@sentry.io/proj" + tracesSampleRate = 1.0 + } + val scope = Scope(options) + + val scopes = mock() + val requestExecution = mock() + val body = "data".toByteArray() + + init { + whenever(scopes.options).thenReturn(options) + doAnswer { (it.arguments[0] as ScopeCallback).run(scope) } + .whenever(scopes) + .configureScope(any()) + + whenever(requestExecution.execute(any(), any())).thenReturn(mock()) + } + + fun create( + config: Sentry.OptionsConfiguration + ): SentrySpanClientHttpRequestInterceptor { + config.configure(options) + return SentrySpanClientHttpRequestInterceptor(scopes) + } + } + + val fixture = Fixture() + + @Test + fun `attaches w3c trace parent header when enabled`() { + val sut = fixture.create { options -> options.isPropagateTraceparent = true } + sut.intercept(fixture.request, fixture.body, fixture.requestExecution) + + assertNotNull(fixture.request.headers.get(SentryTraceHeader.SENTRY_TRACE_HEADER)) + assertNotNull(fixture.request.headers.get(W3CTraceparentHeader.TRACEPARENT_HEADER)) + } + + @Test + fun `does not attach w3c trace parent header when enabled`() { + val sut = fixture.create { options -> options.isPropagateTraceparent = false } + sut.intercept(fixture.request, fixture.body, fixture.requestExecution) + + assertNotNull(fixture.request.headers.get(SentryTraceHeader.SENTRY_TRACE_HEADER)) + assertNull(fixture.request.headers.get(W3CTraceparentHeader.TRACEPARENT_HEADER)) + } +} From 3a18421d3f581231de902ae0c8dcd07c3110611b Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Tue, 2 Sep 2025 10:33:47 +0200 Subject: [PATCH 10/11] Fix test naming --- .../tracing/SentrySpanClientHttpRequestInterceptorTest.kt | 2 +- .../tracing/SentrySpanClientHttpRequestInterceptorTest.kt | 2 +- .../tracing/SentrySpanClientHttpRequestInterceptorTest.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sentry-spring-7/src/test/kotlin/io/sentry/spring7/tracing/SentrySpanClientHttpRequestInterceptorTest.kt b/sentry-spring-7/src/test/kotlin/io/sentry/spring7/tracing/SentrySpanClientHttpRequestInterceptorTest.kt index 1680179592c..85d138604dd 100644 --- a/sentry-spring-7/src/test/kotlin/io/sentry/spring7/tracing/SentrySpanClientHttpRequestInterceptorTest.kt +++ b/sentry-spring-7/src/test/kotlin/io/sentry/spring7/tracing/SentrySpanClientHttpRequestInterceptorTest.kt @@ -68,7 +68,7 @@ class SentrySpanClientHttpRequestInterceptorTest { } @Test - fun `does not attach w3c trace parent header when enabled`() { + fun `does not attach w3c trace parent header when disabled`() { val sut = fixture.create { options -> options.isPropagateTraceparent = false } sut.intercept(fixture.request, fixture.body, fixture.requestExecution) diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentrySpanClientHttpRequestInterceptorTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentrySpanClientHttpRequestInterceptorTest.kt index 05b77f3d837..e3f9ad2dfa9 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentrySpanClientHttpRequestInterceptorTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentrySpanClientHttpRequestInterceptorTest.kt @@ -68,7 +68,7 @@ class SentrySpanClientHttpRequestInterceptorTest { } @Test - fun `does not attach w3c trace parent header when enabled`() { + fun `does not attach w3c trace parent header when disabled`() { val sut = fixture.create { options -> options.isPropagateTraceparent = false } sut.intercept(fixture.request, fixture.body, fixture.requestExecution) diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentrySpanClientHttpRequestInterceptorTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentrySpanClientHttpRequestInterceptorTest.kt index 665c6fdbb9d..a850480099a 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentrySpanClientHttpRequestInterceptorTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentrySpanClientHttpRequestInterceptorTest.kt @@ -68,7 +68,7 @@ class SentrySpanClientHttpRequestInterceptorTest { } @Test - fun `does not attach w3c trace parent header when enabled`() { + fun `does not attach w3c trace parent header when disabled`() { val sut = fixture.create { options -> options.isPropagateTraceparent = false } sut.intercept(fixture.request, fixture.body, fixture.requestExecution) From d00f61eb0530bfee5ffe9173d4f0596e593b909b Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Wed, 10 Sep 2025 08:16:59 +0200 Subject: [PATCH 11/11] Update CHANGELOG.md --- CHANGELOG.md | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c780c0ffd92..40cf29bba00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## Unreleased + +### Features + +- Add support for w3c traceparent header ([#4671](https://github.com/getsentry/sentry-java/pull/4671)) + - This feature is disabled by default. If enabled, outgoing requests will include the w3c `traceparent` header. + - See https://develop.sentry.dev/sdk/telemetry/traces/distributed-tracing/#w3c-trace-context-header for more details. + ```kotlin + Sentry(Android).init(context) { options -> + // ... + options.isPropagateTraceparent = true + } + ``` + ## 8.21.1 ### Fixes @@ -21,15 +35,7 @@ - Replace `Random` implementation with MIT licensed code ([#4664](https://github.com/getsentry/sentry-java/pull/4664)) - Add support for `vars` attribute in `SentryStackFrame` ([#4686](https://github.com/getsentry/sentry-java/pull/4686)) - **Breaking change**: The type of the `vars` attribute has been changed from `Map` to `Map`. -- Add support for w3c traceparent header ([#4671](https://github.com/getsentry/sentry-java/pull/4671)) - - This feature is disabled by default. If enabled, outgoing requests will include the w3c `traceparent` header. - - See https://develop.sentry.dev/sdk/telemetry/traces/distributed-tracing/#w3c-trace-context-header for more details. - ```kotlin - Sentry(Android).init(context) { options -> - // ... - options.isPropagateTraceparent = true - } - ``` + ## 8.20.0 ### Fixes