From f4a473e81af8049d46c8f1a7c55125603f4a463a Mon Sep 17 00:00:00 2001 From: Giannis Gkiortzis <58184179+giortzisg@users.noreply.github.com> Date: Mon, 2 Mar 2026 16:10:30 +0100 Subject: [PATCH 1/4] feat: Add strict trace continuation support Extract org ID from DSN host, add strictTraceContinuation and orgId options, propagate sentry-org_id in baggage, and validate incoming traces per the decision matrix. Closes #5128 --- sentry/src/main/java/io/sentry/Baggage.java | 17 +++- sentry/src/main/java/io/sentry/Dsn.java | 22 +++++ .../java/io/sentry/PropagationContext.java | 35 +++++++ sentry/src/main/java/io/sentry/Scopes.java | 2 +- .../main/java/io/sentry/SentryOptions.java | 46 +++++++++ sentry/src/test/java/io/sentry/DsnTest.kt | 32 ++++++ .../java/io/sentry/PropagationContextTest.kt | 97 +++++++++++++++++++ 7 files changed, 249 insertions(+), 2 deletions(-) diff --git a/sentry/src/main/java/io/sentry/Baggage.java b/sentry/src/main/java/io/sentry/Baggage.java index 5f610a0291..4645df3f3a 100644 --- a/sentry/src/main/java/io/sentry/Baggage.java +++ b/sentry/src/main/java/io/sentry/Baggage.java @@ -186,6 +186,7 @@ public static Baggage fromEvent( baggage.setPublicKey(options.retrieveParsedDsn().getPublicKey()); baggage.setRelease(event.getRelease()); baggage.setEnvironment(event.getEnvironment()); + baggage.setOrgId(options.getEffectiveOrgId()); baggage.setTransaction(transaction); // we don't persist sample rate baggage.setSampleRate(null); @@ -450,6 +451,16 @@ public void setReplayId(final @Nullable String replayId) { set(DSCKeys.REPLAY_ID, replayId); } + @ApiStatus.Internal + public @Nullable String getOrgId() { + return get(DSCKeys.ORG_ID); + } + + @ApiStatus.Internal + public void setOrgId(final @Nullable String orgId) { + set(DSCKeys.ORG_ID, orgId); + } + /** * Sets / updates a value, but only if the baggage is still mutable. * @@ -501,6 +512,7 @@ public void setValuesFromTransaction( if (replayId != null && !SentryId.EMPTY_ID.equals(replayId)) { setReplayId(replayId.toString()); } + setOrgId(sentryOptions.getEffectiveOrgId()); setSampleRate(sampleRate(samplingDecision)); setSampled(StringUtils.toString(sampled(samplingDecision))); setSampleRand(sampleRand(samplingDecision)); @@ -536,6 +548,7 @@ public void setValuesFromScope( if (!SentryId.EMPTY_ID.equals(replayId)) { setReplayId(replayId.toString()); } + setOrgId(options.getEffectiveOrgId()); setTransaction(null); setSampleRate(null); setSampled(null); @@ -632,6 +645,7 @@ public static final class DSCKeys { public static final String SAMPLE_RAND = "sentry-sample_rand"; public static final String SAMPLED = "sentry-sampled"; public static final String REPLAY_ID = "sentry-replay_id"; + public static final String ORG_ID = "sentry-org_id"; public static final List ALL = Arrays.asList( @@ -644,6 +658,7 @@ public static final class DSCKeys { SAMPLE_RATE, SAMPLE_RAND, SAMPLED, - REPLAY_ID); + REPLAY_ID, + ORG_ID); } } diff --git a/sentry/src/main/java/io/sentry/Dsn.java b/sentry/src/main/java/io/sentry/Dsn.java index 836e2c5546..b027a0fa3a 100644 --- a/sentry/src/main/java/io/sentry/Dsn.java +++ b/sentry/src/main/java/io/sentry/Dsn.java @@ -2,15 +2,20 @@ import io.sentry.util.Objects; import java.net.URI; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; final class Dsn { + private static final @NotNull Pattern ORG_ID_PATTERN = Pattern.compile("^o(\\d+)\\."); + private final @NotNull String projectId; private final @Nullable String path; private final @Nullable String secretKey; private final @NotNull String publicKey; private final @NotNull URI sentryUri; + private @Nullable String orgId; /* / The project ID which the authenticated user is bound to. @@ -84,8 +89,25 @@ URI getSentryUri() { sentryUri = new URI( scheme, null, uri.getHost(), uri.getPort(), path + "api/" + projectId, null, null); + + // Extract org ID from host (e.g., "o123.ingest.sentry.io" -> "123") + final String host = uri.getHost(); + if (host != null) { + final Matcher matcher = ORG_ID_PATTERN.matcher(host); + if (matcher.find()) { + orgId = matcher.group(1); + } + } } catch (Throwable e) { throw new IllegalArgumentException(e); } } + + public @Nullable String getOrgId() { + return orgId; + } + + void setOrgId(final @Nullable String orgId) { + this.orgId = orgId; + } } diff --git a/sentry/src/main/java/io/sentry/PropagationContext.java b/sentry/src/main/java/io/sentry/PropagationContext.java index e7d39d35fe..772013ec56 100644 --- a/sentry/src/main/java/io/sentry/PropagationContext.java +++ b/sentry/src/main/java/io/sentry/PropagationContext.java @@ -23,6 +23,14 @@ public static PropagationContext fromHeaders( final @NotNull ILogger logger, final @Nullable String sentryTraceHeaderString, final @Nullable List baggageHeaderStrings) { + return fromHeaders(logger, sentryTraceHeaderString, baggageHeaderStrings, null); + } + + public static @NotNull PropagationContext fromHeaders( + final @NotNull ILogger logger, + final @Nullable String sentryTraceHeaderString, + final @Nullable List baggageHeaderStrings, + final @Nullable SentryOptions options) { if (sentryTraceHeaderString == null) { return new PropagationContext(); } @@ -30,6 +38,12 @@ public static PropagationContext fromHeaders( try { final @NotNull SentryTraceHeader traceHeader = new SentryTraceHeader(sentryTraceHeaderString); final @NotNull Baggage baggage = Baggage.fromHeader(baggageHeaderStrings, logger); + + if (options != null && !shouldContinueTrace(options, baggage)) { + logger.log(SentryLevel.DEBUG, "Not continuing trace due to org ID mismatch."); + return new PropagationContext(); + } + return fromHeaders(traceHeader, baggage, null); } catch (InvalidSentryTraceHeaderException e) { logger.log(SentryLevel.DEBUG, e, "Failed to parse Sentry trace header: %s", e.getMessage()); @@ -149,4 +163,25 @@ public void setSampled(final @Nullable Boolean sampled) { // should never be null since we ensure it in ctor return sampleRand == null ? 0.0 : sampleRand; } + + static boolean shouldContinueTrace( + final @NotNull SentryOptions options, final @Nullable Baggage baggage) { + final @Nullable String sdkOrgId = options.getEffectiveOrgId(); + final @Nullable String baggageOrgId = baggage != null ? baggage.getOrgId() : null; + + // Mismatched org IDs always reject regardless of strict mode + if (sdkOrgId != null && baggageOrgId != null && !sdkOrgId.equals(baggageOrgId)) { + return false; + } + + // In strict mode, both must be present and match (unless both are missing) + if (options.isStrictTraceContinuation()) { + if (sdkOrgId == null && baggageOrgId == null) { + return true; + } + return sdkOrgId != null && sdkOrgId.equals(baggageOrgId); + } + + return true; + } } diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index ee3d55f229..d27d500b2c 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -1135,7 +1135,7 @@ public void reportFullyDisplayed() { final @Nullable String sentryTrace, final @Nullable List baggageHeaders) { @NotNull PropagationContext propagationContext = - PropagationContext.fromHeaders(getOptions().getLogger(), sentryTrace, baggageHeaders); + PropagationContext.fromHeaders(getOptions().getLogger(), sentryTrace, baggageHeaders, getOptions()); configureScope( (scope) -> { scope.withPropagationContext( diff --git a/sentry/src/main/java/io/sentry/SentryOptions.java b/sentry/src/main/java/io/sentry/SentryOptions.java index 14f4acf51f..12660f3a20 100644 --- a/sentry/src/main/java/io/sentry/SentryOptions.java +++ b/sentry/src/main/java/io/sentry/SentryOptions.java @@ -427,6 +427,21 @@ public class SentryOptions { /** Whether to propagate W3C traceparent HTTP header. */ private boolean propagateTraceparent = false; + /** + * Controls whether the SDK requires matching org IDs from incoming baggage to continue a trace. + * When true, both the SDK's org ID and the incoming baggage org ID must be present and match. + * When false, a mismatch between present org IDs will still start a new trace, but missing org + * IDs on either side are tolerated. + */ + private boolean strictTraceContinuation = false; + + /** + * An optional organization ID. The SDK will try to extract it from the DSN in most cases but you + * can provide it explicitly for self-hosted and Relay setups. This value is used for trace + * propagation and for features like {@link #strictTraceContinuation}. + */ + private @Nullable String orgId; + /** Proguard UUID. */ private @Nullable String proguardUuid; @@ -2287,6 +2302,37 @@ public void setPropagateTraceparent(final boolean propagateTraceparent) { this.propagateTraceparent = propagateTraceparent; } + public boolean isStrictTraceContinuation() { + return strictTraceContinuation; + } + + public void setStrictTraceContinuation(final boolean strictTraceContinuation) { + this.strictTraceContinuation = strictTraceContinuation; + } + + public @Nullable String getOrgId() { + return orgId; + } + + public void setOrgId(final @Nullable String orgId) { + this.orgId = orgId; + } + + /** + * Returns the effective org ID, preferring the explicit config option over the DSN-parsed value. + */ + public @Nullable String getEffectiveOrgId() { + if (orgId != null) { + return orgId; + } + try { + final @Nullable String dsnOrgId = retrieveParsedDsn().getOrgId(); + return dsnOrgId; + } catch (Throwable e) { + return null; + } + } + /** * Returns a Proguard UUID. * diff --git a/sentry/src/test/java/io/sentry/DsnTest.kt b/sentry/src/test/java/io/sentry/DsnTest.kt index eaa129c207..7efc07c5a6 100644 --- a/sentry/src/test/java/io/sentry/DsnTest.kt +++ b/sentry/src/test/java/io/sentry/DsnTest.kt @@ -103,4 +103,36 @@ class DsnTest { Dsn("HTTP://publicKey:secretKey@host/path/id") Dsn("HTTPS://publicKey:secretKey@host/path/id") } + + @Test + fun `extracts org id from host`() { + val dsn = Dsn("https://key@o123.ingest.sentry.io/456") + assertEquals("123", dsn.orgId) + } + + @Test + fun `extracts single digit org id from host`() { + val dsn = Dsn("https://key@o1.ingest.us.sentry.io/456") + assertEquals("1", dsn.orgId) + } + + @Test + fun `returns null org id when host has no org prefix`() { + val dsn = Dsn("https://key@sentry.io/456") + assertNull(dsn.orgId) + } + + @Test + fun `returns null org id for non-standard host`() { + val dsn = Dsn("http://key@localhost:9000/456") + assertNull(dsn.orgId) + } + + @Test + fun `org id can be overridden via setter`() { + val dsn = Dsn("https://key@o123.ingest.sentry.io/456") + assertEquals("123", dsn.orgId) + dsn.setOrgId("999") + assertEquals("999", dsn.orgId) + } } diff --git a/sentry/src/test/java/io/sentry/PropagationContextTest.kt b/sentry/src/test/java/io/sentry/PropagationContextTest.kt index 8e83dec4de..3431a88f37 100644 --- a/sentry/src/test/java/io/sentry/PropagationContextTest.kt +++ b/sentry/src/test/java/io/sentry/PropagationContextTest.kt @@ -1,7 +1,9 @@ package io.sentry import kotlin.test.Test +import kotlin.test.assertEquals import kotlin.test.assertFalse +import kotlin.test.assertNotEquals import kotlin.test.assertNotNull import kotlin.test.assertTrue @@ -42,4 +44,99 @@ class PropagationContextTest { assertTrue(propagationContext.baggage.isMutable) assertFalse(propagationContext.baggage.isShouldFreeze) } + + // Decision matrix tests for shouldContinueTrace + + private val incomingTraceId = "bc6d53f15eb88f4320054569b8c553d4" + private val sentryTrace = "bc6d53f15eb88f4320054569b8c553d4-b72fa28504b07285-1" + + private fun makeOptions(dsnOrgId: String?, explicitOrgId: String? = null, strict: Boolean = false): SentryOptions { + val options = SentryOptions() + if (dsnOrgId != null) { + options.dsn = "https://key@o$dsnOrgId.ingest.sentry.io/123" + } else { + options.dsn = "https://key@sentry.io/123" + } + options.orgId = explicitOrgId + options.isStrictTraceContinuation = strict + return options + } + + private fun makeBaggage(orgId: String?): String { + val parts = mutableListOf("sentry-trace_id=$incomingTraceId") + if (orgId != null) { + parts.add("sentry-org_id=$orgId") + } + return parts.joinToString(",") + } + + @Test + fun `strict=false, matching orgs - continues trace`() { + val options = makeOptions(dsnOrgId = "1", strict = false) + val pc = PropagationContext.fromHeaders(NoOpLogger.getInstance(), sentryTrace, makeBaggage("1"), options) + assertEquals(incomingTraceId, pc.traceId.toString()) + } + + @Test + fun `strict=false, baggage missing org - continues trace`() { + val options = makeOptions(dsnOrgId = "1", strict = false) + val pc = PropagationContext.fromHeaders(NoOpLogger.getInstance(), sentryTrace, makeBaggage(null), options) + assertEquals(incomingTraceId, pc.traceId.toString()) + } + + @Test + fun `strict=false, sdk missing org - continues trace`() { + val options = makeOptions(dsnOrgId = null, strict = false) + val pc = PropagationContext.fromHeaders(NoOpLogger.getInstance(), sentryTrace, makeBaggage("1"), options) + assertEquals(incomingTraceId, pc.traceId.toString()) + } + + @Test + fun `strict=false, both missing org - continues trace`() { + val options = makeOptions(dsnOrgId = null, strict = false) + val pc = PropagationContext.fromHeaders(NoOpLogger.getInstance(), sentryTrace, makeBaggage(null), options) + assertEquals(incomingTraceId, pc.traceId.toString()) + } + + @Test + fun `strict=false, mismatched orgs - starts new trace`() { + val options = makeOptions(dsnOrgId = "2", strict = false) + val pc = PropagationContext.fromHeaders(NoOpLogger.getInstance(), sentryTrace, makeBaggage("1"), options) + assertNotEquals(incomingTraceId, pc.traceId.toString()) + } + + @Test + fun `strict=true, matching orgs - continues trace`() { + val options = makeOptions(dsnOrgId = "1", strict = true) + val pc = PropagationContext.fromHeaders(NoOpLogger.getInstance(), sentryTrace, makeBaggage("1"), options) + assertEquals(incomingTraceId, pc.traceId.toString()) + } + + @Test + fun `strict=true, baggage missing org - starts new trace`() { + val options = makeOptions(dsnOrgId = "1", strict = true) + val pc = PropagationContext.fromHeaders(NoOpLogger.getInstance(), sentryTrace, makeBaggage(null), options) + assertNotEquals(incomingTraceId, pc.traceId.toString()) + } + + @Test + fun `strict=true, sdk missing org - starts new trace`() { + val options = makeOptions(dsnOrgId = null, strict = true) + val pc = PropagationContext.fromHeaders(NoOpLogger.getInstance(), sentryTrace, makeBaggage("1"), options) + assertNotEquals(incomingTraceId, pc.traceId.toString()) + } + + @Test + fun `strict=true, both missing org - continues trace`() { + val options = makeOptions(dsnOrgId = null, strict = true) + val pc = PropagationContext.fromHeaders(NoOpLogger.getInstance(), sentryTrace, makeBaggage(null), options) + assertEquals(incomingTraceId, pc.traceId.toString()) + } + + @Test + fun `strict=true, mismatched orgs - starts new trace`() { + val options = makeOptions(dsnOrgId = "2", strict = true) + val pc = PropagationContext.fromHeaders(NoOpLogger.getInstance(), sentryTrace, makeBaggage("1"), options) + assertNotEquals(incomingTraceId, pc.traceId.toString()) + } } From f8db414d63e64520766a570d7548affeb4643ab9 Mon Sep 17 00:00:00 2001 From: Sentry Github Bot Date: Mon, 2 Mar 2026 15:17:51 +0000 Subject: [PATCH 2/4] Format code --- sentry/src/main/java/io/sentry/Scopes.java | 3 +- .../java/io/sentry/PropagationContextTest.kt | 86 ++++++++++++++++--- 2 files changed, 77 insertions(+), 12 deletions(-) diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index d27d500b2c..127b78ae10 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -1135,7 +1135,8 @@ public void reportFullyDisplayed() { final @Nullable String sentryTrace, final @Nullable List baggageHeaders) { @NotNull PropagationContext propagationContext = - PropagationContext.fromHeaders(getOptions().getLogger(), sentryTrace, baggageHeaders, getOptions()); + PropagationContext.fromHeaders( + getOptions().getLogger(), sentryTrace, baggageHeaders, getOptions()); configureScope( (scope) -> { scope.withPropagationContext( diff --git a/sentry/src/test/java/io/sentry/PropagationContextTest.kt b/sentry/src/test/java/io/sentry/PropagationContextTest.kt index 3431a88f37..75068c5525 100644 --- a/sentry/src/test/java/io/sentry/PropagationContextTest.kt +++ b/sentry/src/test/java/io/sentry/PropagationContextTest.kt @@ -50,7 +50,11 @@ class PropagationContextTest { private val incomingTraceId = "bc6d53f15eb88f4320054569b8c553d4" private val sentryTrace = "bc6d53f15eb88f4320054569b8c553d4-b72fa28504b07285-1" - private fun makeOptions(dsnOrgId: String?, explicitOrgId: String? = null, strict: Boolean = false): SentryOptions { + private fun makeOptions( + dsnOrgId: String?, + explicitOrgId: String? = null, + strict: Boolean = false, + ): SentryOptions { val options = SentryOptions() if (dsnOrgId != null) { options.dsn = "https://key@o$dsnOrgId.ingest.sentry.io/123" @@ -73,70 +77,130 @@ class PropagationContextTest { @Test fun `strict=false, matching orgs - continues trace`() { val options = makeOptions(dsnOrgId = "1", strict = false) - val pc = PropagationContext.fromHeaders(NoOpLogger.getInstance(), sentryTrace, makeBaggage("1"), options) + val pc = + PropagationContext.fromHeaders( + NoOpLogger.getInstance(), + sentryTrace, + makeBaggage("1"), + options, + ) assertEquals(incomingTraceId, pc.traceId.toString()) } @Test fun `strict=false, baggage missing org - continues trace`() { val options = makeOptions(dsnOrgId = "1", strict = false) - val pc = PropagationContext.fromHeaders(NoOpLogger.getInstance(), sentryTrace, makeBaggage(null), options) + val pc = + PropagationContext.fromHeaders( + NoOpLogger.getInstance(), + sentryTrace, + makeBaggage(null), + options, + ) assertEquals(incomingTraceId, pc.traceId.toString()) } @Test fun `strict=false, sdk missing org - continues trace`() { val options = makeOptions(dsnOrgId = null, strict = false) - val pc = PropagationContext.fromHeaders(NoOpLogger.getInstance(), sentryTrace, makeBaggage("1"), options) + val pc = + PropagationContext.fromHeaders( + NoOpLogger.getInstance(), + sentryTrace, + makeBaggage("1"), + options, + ) assertEquals(incomingTraceId, pc.traceId.toString()) } @Test fun `strict=false, both missing org - continues trace`() { val options = makeOptions(dsnOrgId = null, strict = false) - val pc = PropagationContext.fromHeaders(NoOpLogger.getInstance(), sentryTrace, makeBaggage(null), options) + val pc = + PropagationContext.fromHeaders( + NoOpLogger.getInstance(), + sentryTrace, + makeBaggage(null), + options, + ) assertEquals(incomingTraceId, pc.traceId.toString()) } @Test fun `strict=false, mismatched orgs - starts new trace`() { val options = makeOptions(dsnOrgId = "2", strict = false) - val pc = PropagationContext.fromHeaders(NoOpLogger.getInstance(), sentryTrace, makeBaggage("1"), options) + val pc = + PropagationContext.fromHeaders( + NoOpLogger.getInstance(), + sentryTrace, + makeBaggage("1"), + options, + ) assertNotEquals(incomingTraceId, pc.traceId.toString()) } @Test fun `strict=true, matching orgs - continues trace`() { val options = makeOptions(dsnOrgId = "1", strict = true) - val pc = PropagationContext.fromHeaders(NoOpLogger.getInstance(), sentryTrace, makeBaggage("1"), options) + val pc = + PropagationContext.fromHeaders( + NoOpLogger.getInstance(), + sentryTrace, + makeBaggage("1"), + options, + ) assertEquals(incomingTraceId, pc.traceId.toString()) } @Test fun `strict=true, baggage missing org - starts new trace`() { val options = makeOptions(dsnOrgId = "1", strict = true) - val pc = PropagationContext.fromHeaders(NoOpLogger.getInstance(), sentryTrace, makeBaggage(null), options) + val pc = + PropagationContext.fromHeaders( + NoOpLogger.getInstance(), + sentryTrace, + makeBaggage(null), + options, + ) assertNotEquals(incomingTraceId, pc.traceId.toString()) } @Test fun `strict=true, sdk missing org - starts new trace`() { val options = makeOptions(dsnOrgId = null, strict = true) - val pc = PropagationContext.fromHeaders(NoOpLogger.getInstance(), sentryTrace, makeBaggage("1"), options) + val pc = + PropagationContext.fromHeaders( + NoOpLogger.getInstance(), + sentryTrace, + makeBaggage("1"), + options, + ) assertNotEquals(incomingTraceId, pc.traceId.toString()) } @Test fun `strict=true, both missing org - continues trace`() { val options = makeOptions(dsnOrgId = null, strict = true) - val pc = PropagationContext.fromHeaders(NoOpLogger.getInstance(), sentryTrace, makeBaggage(null), options) + val pc = + PropagationContext.fromHeaders( + NoOpLogger.getInstance(), + sentryTrace, + makeBaggage(null), + options, + ) assertEquals(incomingTraceId, pc.traceId.toString()) } @Test fun `strict=true, mismatched orgs - starts new trace`() { val options = makeOptions(dsnOrgId = "2", strict = true) - val pc = PropagationContext.fromHeaders(NoOpLogger.getInstance(), sentryTrace, makeBaggage("1"), options) + val pc = + PropagationContext.fromHeaders( + NoOpLogger.getInstance(), + sentryTrace, + makeBaggage("1"), + options, + ) assertNotEquals(incomingTraceId, pc.traceId.toString()) } } From f18622dd35c0488ca9de33caba2f819a2f00de8f Mon Sep 17 00:00:00 2001 From: Giannis Gkiortzis <58184179+giortzisg@users.noreply.github.com> Date: Mon, 2 Mar 2026 17:01:47 +0100 Subject: [PATCH 3/4] Add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dba0f4dcdf..49b0f666c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Features +- Add strict trace continuation support ([#5136](https://github.com/getsentry/sentry-java/pull/5136)) - Create `sentry-opentelemetry-otlp` and `sentry-opentelemetry-otlp-spring` modules for combining OpenTelemetry SDK OTLP export with Sentry SDK ([#5100](https://github.com/getsentry/sentry-java/pull/5100)) - OpenTelemetry is configured to send spans to Sentry directly using an OTLP endpoint. - Sentry only uses trace and span ID from OpenTelemetry (via `OpenTelemetryOtlpEventProcessor`) but will not send spans through OpenTelemetry nor use OpenTelemetry `Context` for `Scopes` propagation. From 96a5ff2430fabdb939c5b167d50810a62ccd0a73 Mon Sep 17 00:00:00 2001 From: Giannis Gkiortzis <58184179+giortzisg@users.noreply.github.com> Date: Tue, 3 Mar 2026 10:12:09 +0100 Subject: [PATCH 4/4] Update API surface file for strict trace continuation Add public API declarations for new org ID and strict trace continuation methods on Baggage, PropagationContext, and SentryOptions. Co-Authored-By: Claude Opus 4.6 --- sentry/api/sentry.api | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 4399b191d2..aee0387d8a 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -47,6 +47,7 @@ public final class io/sentry/Baggage { public static fun fromHeader (Ljava/util/List;ZLio/sentry/ILogger;)Lio/sentry/Baggage; public fun get (Ljava/lang/String;)Ljava/lang/String; public fun getEnvironment ()Ljava/lang/String; + public fun getOrgId ()Ljava/lang/String; public fun getPublicKey ()Ljava/lang/String; public fun getRelease ()Ljava/lang/String; public fun getReplayId ()Ljava/lang/String; @@ -62,6 +63,7 @@ public final class io/sentry/Baggage { public fun isShouldFreeze ()Z public fun set (Ljava/lang/String;Ljava/lang/String;)V public fun setEnvironment (Ljava/lang/String;)V + public fun setOrgId (Ljava/lang/String;)V public fun setPublicKey (Ljava/lang/String;)V public fun setRelease (Ljava/lang/String;)V public fun setReplayId (Ljava/lang/String;)V @@ -81,6 +83,7 @@ public final class io/sentry/Baggage { public final class io/sentry/Baggage$DSCKeys { public static final field ALL Ljava/util/List; public static final field ENVIRONMENT Ljava/lang/String; + public static final field ORG_ID Ljava/lang/String; public static final field PUBLIC_KEY Ljava/lang/String; public static final field RELEASE Ljava/lang/String; public static final field REPLAY_ID Ljava/lang/String; @@ -2230,6 +2233,7 @@ public final class io/sentry/PropagationContext { public static fun fromExistingTrace (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Ljava/lang/Double;)Lio/sentry/PropagationContext; public static fun fromHeaders (Lio/sentry/ILogger;Ljava/lang/String;Ljava/lang/String;)Lio/sentry/PropagationContext; public static fun fromHeaders (Lio/sentry/ILogger;Ljava/lang/String;Ljava/util/List;)Lio/sentry/PropagationContext; + public static fun fromHeaders (Lio/sentry/ILogger;Ljava/lang/String;Ljava/util/List;Lio/sentry/SentryOptions;)Lio/sentry/PropagationContext; public static fun fromHeaders (Lio/sentry/SentryTraceHeader;Lio/sentry/Baggage;Lio/sentry/SpanId;)Lio/sentry/PropagationContext; public fun getBaggage ()Lio/sentry/Baggage; public fun getParentSpanId ()Lio/sentry/SpanId; @@ -3510,6 +3514,7 @@ public class io/sentry/SentryOptions { public fun getDistribution ()Lio/sentry/SentryOptions$DistributionOptions; public fun getDistributionController ()Lio/sentry/IDistributionApi; public fun getDsn ()Ljava/lang/String; + public fun getEffectiveOrgId ()Ljava/lang/String; public fun getEnvelopeDiskCache ()Lio/sentry/cache/IEnvelopeCache; public fun getEnvelopeReader ()Lio/sentry/IEnvelopeReader; public fun getEnvironment ()Ljava/lang/String; @@ -3550,6 +3555,7 @@ public class io/sentry/SentryOptions { public fun getOnOversizedEvent ()Lio/sentry/SentryOptions$OnOversizedEventCallback; public fun getOpenTelemetryMode ()Lio/sentry/SentryOpenTelemetryMode; public fun getOptionsObservers ()Ljava/util/List; + public fun getOrgId ()Ljava/lang/String; public fun getOutboxPath ()Ljava/lang/String; public fun getPerformanceCollectors ()Ljava/util/List; public fun getProfileLifecycle ()Lio/sentry/ProfileLifecycle; @@ -3621,6 +3627,7 @@ public class io/sentry/SentryOptions { public fun isSendDefaultPii ()Z public fun isSendModules ()Z public fun isStartProfilerOnAppStart ()Z + public fun isStrictTraceContinuation ()Z public fun isTraceOptionsRequests ()Z public fun isTraceSampling ()Z public fun isTracingEnabled ()Z @@ -3704,6 +3711,7 @@ public class io/sentry/SentryOptions { public fun setOnDiscard (Lio/sentry/SentryOptions$OnDiscardCallback;)V public fun setOnOversizedEvent (Lio/sentry/SentryOptions$OnOversizedEventCallback;)V public fun setOpenTelemetryMode (Lio/sentry/SentryOpenTelemetryMode;)V + public fun setOrgId (Ljava/lang/String;)V public fun setPrintUncaughtStackTrace (Z)V public fun setProfileLifecycle (Lio/sentry/ProfileLifecycle;)V public fun setProfileSessionSampleRate (Ljava/lang/Double;)V @@ -3736,6 +3744,7 @@ public class io/sentry/SentryOptions { public fun setSpotlightConnectionUrl (Ljava/lang/String;)V public fun setSslSocketFactory (Ljavax/net/ssl/SSLSocketFactory;)V public fun setStartProfilerOnAppStart (Z)V + public fun setStrictTraceContinuation (Z)V public fun setTag (Ljava/lang/String;Ljava/lang/String;)V public fun setThreadChecker (Lio/sentry/util/thread/IThreadChecker;)V public fun setTraceOptionsRequests (Z)V