Skip to content

Commit ebe8c4c

Browse files
giortzisgclaude
andcommitted
fix: Address PR review comments for strict trace continuation
- Remove duplicated org ID check in PropagationContext.fromHeaders, pass options through to the single-check overload instead - Add debug log when trace is not continued in the SentryTraceHeader overload - Handle empty/blank org ID strings in shouldContinueTrace to avoid silently breaking traces - Update OtelSentrySpanProcessor to use PropagationContext.fromHeaders with options for org_id validation - Rename ExternalOptions property key to enable-strict-trace-continuation (matching the enable- prefix convention for newer options) - Update ExternalOptionsTest to use the new property key - Add strict-trace-continuation and org-id properties to all 3 Spring Boot SentryAutoConfigurationTest modules - Improve CHANGELOG entry with detailed customer-facing descriptions and configuration examples for all options Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 58bfc8e commit ebe8c4c

File tree

8 files changed

+40
-16
lines changed

8 files changed

+40
-16
lines changed

CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77
- Prevent cross-organization trace continuation ([#5136](https://github.com/getsentry/sentry-java/pull/5136))
88
- By default, the SDK now extracts the organization ID from the DSN (e.g. `o123.ingest.sentry.io`) and compares it with the `sentry-org_id` value in incoming baggage headers. When the two differ, the SDK starts a fresh trace instead of continuing the foreign one. This guards against accidentally linking traces across organizations.
9-
- New option `strictTraceContinuation` (default `false`): when enabled, both the SDK's org ID **and** the incoming baggage org ID must be present and match for a trace to be continued. Traces with a missing org ID on either side are rejected.
10-
- New option `orgId`: allows explicitly setting the organization ID for self-hosted and Relay setups where it cannot be extracted from the DSN. Configurable via code, `sentry.properties` (`org-id`), or Android manifest (`io.sentry.org-id`).
9+
- New option `enableStrictTraceContinuation` (default `false`): when enabled, both the SDK's org ID **and** the incoming baggage org ID must be present and match for a trace to be continued. Traces with a missing org ID on either side are rejected. Configurable via code (`setStrictTraceContinuation(true)`), `sentry.properties` (`enable-strict-trace-continuation=true`), Android manifest (`io.sentry.strict-trace-continuation`), or Spring Boot (`sentry.strict-trace-continuation=true`).
10+
- New option `orgId`: allows explicitly setting the organization ID for self-hosted and Relay setups where it cannot be extracted from the DSN. Configurable via code (`setOrgId("123")`), `sentry.properties` (`org-id=123`), Android manifest (`io.sentry.org-id`), or Spring Boot (`sentry.org-id=123`).
1111

1212
## 8.34.1
1313

sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSentrySpanProcessor.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import io.sentry.SentryEvent;
2323
import io.sentry.SentryLevel;
2424
import io.sentry.SentryLongDate;
25+
import io.sentry.SentryOptions;
2526
import io.sentry.SentryTraceHeader;
2627
import io.sentry.SpanId;
2728
import io.sentry.TracesSamplingDecision;
@@ -94,9 +95,16 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri
9495
}
9596
}
9697

97-
final @NotNull PropagationContext propagationContext =
98-
new PropagationContext(
99-
new SentryId(traceId), sentrySpanId, sentryParentSpanId, baggage, sampled);
98+
final @NotNull SentryOptions sentryOptions = scopes.getOptions();
99+
final @NotNull PropagationContext propagationContext;
100+
if (sentryTraceHeader != null) {
101+
propagationContext =
102+
PropagationContext.fromHeaders(sentryTraceHeader, baggage, sentrySpanId, sentryOptions);
103+
} else {
104+
propagationContext =
105+
new PropagationContext(
106+
new SentryId(traceId), sentrySpanId, sentryParentSpanId, baggage, sampled);
107+
}
100108

101109
baggage = propagationContext.getBaggage();
102110
baggage.setValuesFromSamplingDecision(samplingDecision);

sentry-spring-boot-4/src/test/kotlin/io/sentry/spring/boot4/SentryAutoConfigurationTest.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,8 @@ class SentryAutoConfigurationTest {
242242
"sentry.cron.default-failure-issue-threshold=40",
243243
"sentry.cron.default-recovery-threshold=50",
244244
"sentry.logs.enabled=true",
245+
"sentry.strict-trace-continuation=true",
246+
"sentry.org-id=12345",
245247
)
246248
.run {
247249
val options = it.getBean(SentryProperties::class.java)
@@ -296,6 +298,8 @@ class SentryAutoConfigurationTest {
296298
assertThat(options.cron!!.defaultFailureIssueThreshold).isEqualTo(40L)
297299
assertThat(options.cron!!.defaultRecoveryThreshold).isEqualTo(50L)
298300
assertThat(options.logs.isEnabled).isEqualTo(true)
301+
assertThat(options.isStrictTraceContinuation).isEqualTo(true)
302+
assertThat(options.orgId).isEqualTo("12345")
299303
}
300304
}
301305

sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentryAutoConfigurationTest.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,8 @@ class SentryAutoConfigurationTest {
249249
"sentry.profile-session-sample-rate=1.0",
250250
"sentry.profiling-traces-dir-path=tmp/sentry/profiling-traces",
251251
"sentry.profile-lifecycle=TRACE",
252+
"sentry.strict-trace-continuation=true",
253+
"sentry.org-id=12345",
252254
)
253255
.run {
254256
val options = it.getBean(SentryProperties::class.java)
@@ -307,6 +309,8 @@ class SentryAutoConfigurationTest {
307309
assertThat(options.profilingTracesDirPath)
308310
.startsWith(File("tmp/sentry/profiling-traces").absolutePath)
309311
assertThat(options.profileLifecycle).isEqualTo(ProfileLifecycle.TRACE)
312+
assertThat(options.isStrictTraceContinuation).isEqualTo(true)
313+
assertThat(options.orgId).isEqualTo("12345")
310314
}
311315
}
312316

sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentryAutoConfigurationTest.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,8 @@ class SentryAutoConfigurationTest {
247247
"sentry.profile-session-sample-rate=1.0",
248248
"sentry.profiling-traces-dir-path=tmp/sentry/profiling-traces",
249249
"sentry.profile-lifecycle=TRACE",
250+
"sentry.strict-trace-continuation=true",
251+
"sentry.org-id=12345",
250252
)
251253
.run {
252254
val options = it.getBean(SentryProperties::class.java)
@@ -305,6 +307,8 @@ class SentryAutoConfigurationTest {
305307
assertThat(options.profilingTracesDirPath)
306308
.startsWith(File("tmp/sentry/profiling-traces").absolutePath)
307309
assertThat(options.profileLifecycle).isEqualTo(ProfileLifecycle.TRACE)
310+
assertThat(options.isStrictTraceContinuation).isEqualTo(true)
311+
assertThat(options.orgId).isEqualTo("12345")
308312
}
309313
}
310314

sentry/src/main/java/io/sentry/ExternalOptions.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ public final class ExternalOptions {
222222
}
223223

224224
options.setStrictTraceContinuation(
225-
propertiesProvider.getBooleanProperty("strict-trace-continuation"));
225+
propertiesProvider.getBooleanProperty("enable-strict-trace-continuation"));
226226
options.setOrgId(propertiesProvider.getProperty("org-id"));
227227

228228
options.setEnableSpotlight(propertiesProvider.getBooleanProperty("enable-spotlight"));

sentry/src/main/java/io/sentry/PropagationContext.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,7 @@ public static PropagationContext fromHeaders(
3333
final @NotNull SentryTraceHeader traceHeader = new SentryTraceHeader(sentryTraceHeaderString);
3434
final @NotNull Baggage baggage = Baggage.fromHeader(baggageHeaderStrings, logger);
3535

36-
if (options != null && !shouldContinueTrace(options, baggage)) {
37-
logger.log(SentryLevel.DEBUG, "Not continuing trace due to org ID mismatch.");
38-
return new PropagationContext();
39-
}
40-
41-
return fromHeaders(traceHeader, baggage, null, null);
36+
return fromHeaders(traceHeader, baggage, null, options);
4237
} catch (InvalidSentryTraceHeaderException e) {
4338
logger.log(SentryLevel.DEBUG, e, "Failed to parse Sentry trace header: %s", e.getMessage());
4439
return new PropagationContext();
@@ -51,6 +46,9 @@ public static PropagationContext fromHeaders(
5146
final @Nullable SpanId spanId,
5247
final @Nullable SentryOptions options) {
5348
if (options != null && !shouldContinueTrace(options, baggage)) {
49+
options
50+
.getLogger()
51+
.log(SentryLevel.DEBUG, "Not continuing trace due to org ID mismatch.");
5452
return new PropagationContext();
5553
}
5654

@@ -165,8 +163,14 @@ public void setSampled(final @Nullable Boolean sampled) {
165163

166164
static boolean shouldContinueTrace(
167165
final @NotNull SentryOptions options, final @Nullable Baggage baggage) {
168-
final @Nullable String sdkOrgId = options.getEffectiveOrgId();
169-
final @Nullable String baggageOrgId = baggage != null ? baggage.getOrgId() : null;
166+
final @Nullable String rawSdkOrgId = options.getEffectiveOrgId();
167+
final @Nullable String sdkOrgId =
168+
(rawSdkOrgId != null && !rawSdkOrgId.trim().isEmpty()) ? rawSdkOrgId.trim() : null;
169+
final @Nullable String rawBaggageOrgId = baggage != null ? baggage.getOrgId() : null;
170+
final @Nullable String baggageOrgId =
171+
(rawBaggageOrgId != null && !rawBaggageOrgId.trim().isEmpty())
172+
? rawBaggageOrgId.trim()
173+
: null;
170174

171175
// Mismatched org IDs always reject regardless of strict mode
172176
if (sdkOrgId != null && baggageOrgId != null && !sdkOrgId.equals(baggageOrgId)) {

sentry/src/test/java/io/sentry/ExternalOptionsTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -451,14 +451,14 @@ class ExternalOptionsTest {
451451

452452
@Test
453453
fun `creates options with strictTraceContinuation set to true`() {
454-
withPropertiesFile("strict-trace-continuation=true") { options ->
454+
withPropertiesFile("enable-strict-trace-continuation=true") { options ->
455455
assertTrue(options.isStrictTraceContinuation == true)
456456
}
457457
}
458458

459459
@Test
460460
fun `creates options with strictTraceContinuation set to false`() {
461-
withPropertiesFile("strict-trace-continuation=false") { options ->
461+
withPropertiesFile("enable-strict-trace-continuation=false") { options ->
462462
assertTrue(options.isStrictTraceContinuation == false)
463463
}
464464
}

0 commit comments

Comments
 (0)