feat: Add strict trace continuation support#5136
Conversation
Semver Impact of This PR🟡 Minor (new features) 📋 Changelog PreviewThis is how your changes will appear in the changelog. New Features ✨
Internal Changes 🔧
🤖 This preview updates automatically when you update the PR. |
|
Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 27d7cf8 | 314.17 ms | 347.00 ms | 32.83 ms |
| ff8eea4 | 313.42 ms | 337.08 ms | 23.66 ms |
| b750b96 | 408.98 ms | 480.32 ms | 71.34 ms |
| 0eaac1e | 322.53 ms | 389.31 ms | 66.78 ms |
| 18c0bc2 | 306.73 ms | 349.77 ms | 43.03 ms |
| abf451a | 332.82 ms | 403.67 ms | 70.85 ms |
| 62b579c | 349.26 ms | 426.26 ms | 77.00 ms |
| f064536 | 335.52 ms | 408.79 ms | 73.27 ms |
| 70118e9 | 380.00 ms | 475.72 ms | 95.72 ms |
| 85d7417 | 347.21 ms | 394.35 ms | 47.15 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 27d7cf8 | 1.58 MiB | 2.12 MiB | 549.42 KiB |
| ff8eea4 | 1.58 MiB | 2.28 MiB | 718.64 KiB |
| b750b96 | 1.58 MiB | 2.10 MiB | 533.19 KiB |
| 0eaac1e | 1.58 MiB | 2.19 MiB | 619.17 KiB |
| 18c0bc2 | 1.58 MiB | 2.13 MiB | 557.33 KiB |
| abf451a | 1.58 MiB | 2.20 MiB | 635.29 KiB |
| 62b579c | 0 B | 0 B | 0 B |
| f064536 | 1.58 MiB | 2.20 MiB | 633.90 KiB |
| 70118e9 | 1.58 MiB | 2.29 MiB | 719.84 KiB |
| 85d7417 | 1.58 MiB | 2.10 MiB | 533.44 KiB |
Previous results on branch: feat/strict-trace-continuation
Startup times
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 79a9407 | 348.85 ms | 428.10 ms | 79.25 ms |
| 455f13a | 320.20 ms | 367.52 ms | 47.33 ms |
| f4b6f75 | 392.12 ms | 440.69 ms | 48.57 ms |
| bbab8da | 309.58 ms | 363.22 ms | 53.64 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 79a9407 | 1.58 MiB | 2.29 MiB | 723.70 KiB |
| 455f13a | 0 B | 0 B | 0 B |
| f4b6f75 | 0 B | 0 B | 0 B |
| bbab8da | 1.58 MiB | 2.29 MiB | 723.51 KiB |
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
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 <noreply@anthropic.com>
- Make Dsn.orgId final, remove unnecessary setter - Fix test signatures to use List<String> for baggage headers - Add strictTraceContinuation and orgId to ExternalOptions and merge() - Add options to ManifestMetadataReader for Android manifest config - Use Sentry.getCurrentScopes().getOptions() in legacy fromHeaders overload - Improve CHANGELOG description with details about new options - Update API surface file for ExternalOptions changes Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add comment to empty catch block in PropagationContext to satisfy -Werror - Add setOrgId setter to Dsn class (remove final modifier on orgId field) to support the existing test for org ID override Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
457e177 to
e30064c
Compare
…x OTel overload, add option tests, make Dsn.orgId final - Remove PropagationContext.fromHeaders overload without SentryOptions; all callers now pass options (or null) explicitly instead of relying on Sentry.getCurrentScopes() - Add SentryOptions parameter to the OTel-facing fromHeaders(SentryTraceHeader, Baggage, SpanId) overload so OpenTelemetry integrations also check orgId - Make Dsn.orgId final and remove the setter — orgId is only set during DSN parsing in the constructor - Add tests for strictTraceContinuation and orgId options in ExternalOptionsTest, SentryOptionsTest, and ManifestMetadataReaderTest - Improve CHANGELOG entry with customer-facing description - Update API declarations (apiDump) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sentry Build Distribution
|
adinauer
left a comment
There was a problem hiding this comment.
Please also change SentryAutoConfigurationTest.kt in all 3 spring boot modules.
I assume it's OK to not backfill org ID on a frozen baggage (i.e. incoming baggage without org ID).
...try/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java
Outdated
Show resolved
Hide resolved
- 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>
Sentry Build Distribution
|
Update the trace-continuation rejection log message to cover all strict org ID validation failures, including missing org IDs, not just mismatches. Co-Authored-By: Claude <noreply@anthropic.com>
Rename the Android manifest option to io.sentry.strict-trace-continuation.enabled to align with existing enabled-style manifest flags. Update changelog documentation to match the new Android key. Co-Authored-By: Claude <noreply@anthropic.com>
Annotate SentryOptions.getEffectiveOrgId with ApiStatus.Internal since it is used as an internal helper for trace propagation org ID resolution. Co-Authored-By: Claude <noreply@anthropic.com>
Move strict trace continuation org-id validation logic to TracingUtils so it can be reused by tracing entry points. Update PropagationContext to call the shared helper and add dedicated TracingUtils tests for strict/non-strict org-id continuation outcomes. Co-Authored-By: Claude <noreply@anthropic.com>
Apply strict trace continuation checks in all OpenTelemetry propagator extract paths before creating remote parent span context. When org-id validation fails, return the original context and ignore incoming sentry-trace and baggage to keep propagation behavior aligned with strict continuation requirements. Add rejection tests for OtelSentryPropagator, deprecated SentryPropagator, and OpenTelemetryOtlpPropagator. Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Empty/blank
orgIdbypasses DSN fallback silentlygetEffectiveOrgId()now treats empty or whitespace-only configured org IDs as unset so DSN org ID fallback is used, and a unit test was added to cover empty/blank inputs.
Or push these changes by commenting:
@cursor push 1b0f711f6d
Preview (1b0f711f6d)
diff --git a/sentry/src/main/java/io/sentry/SentryOptions.java b/sentry/src/main/java/io/sentry/SentryOptions.java
--- a/sentry/src/main/java/io/sentry/SentryOptions.java
+++ b/sentry/src/main/java/io/sentry/SentryOptions.java
@@ -2337,7 +2337,7 @@
*/
@ApiStatus.Internal
public @Nullable String getEffectiveOrgId() {
- if (orgId != null) {
+ if (orgId != null && !orgId.trim().isEmpty()) {
return orgId;
}
try {
diff --git a/sentry/src/test/java/io/sentry/SentryOptionsTest.kt b/sentry/src/test/java/io/sentry/SentryOptionsTest.kt
--- a/sentry/src/test/java/io/sentry/SentryOptionsTest.kt
+++ b/sentry/src/test/java/io/sentry/SentryOptionsTest.kt
@@ -1020,6 +1020,19 @@
}
@Test
+ fun `getEffectiveOrgId falls back to DSN org id when explicit orgId is empty or blank`() {
+ val emptyOrgIdOptions = SentryOptions()
+ emptyOrgIdOptions.dsn = "https://key@o123.ingest.sentry.io/456"
+ emptyOrgIdOptions.orgId = ""
+ assertEquals("123", emptyOrgIdOptions.effectiveOrgId)
+
+ val blankOrgIdOptions = SentryOptions()
+ blankOrgIdOptions.dsn = "https://key@o123.ingest.sentry.io/456"
+ blankOrgIdOptions.orgId = " "
+ assertEquals("123", blankOrgIdOptions.effectiveOrgId)
+ }
+
+ @Test
fun `getEffectiveOrgId falls back to DSN org id`() {
val options = SentryOptions()
options.dsn = "https://key@o123.ingest.sentry.io/456"This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.
| } catch (Throwable e) { | ||
| return null; | ||
| } | ||
| } |
There was a problem hiding this comment.
Empty/blank orgId bypasses DSN fallback silently
High Severity
getEffectiveOrgId() only checks orgId != null before returning it, so an empty or whitespace-only string (e.g., from org-id= in properties) is returned as-is. This prevents the DSN-extracted org ID fallback from ever being reached. The empty value then propagates via Baggage.setOrgId(options.getEffectiveOrgId()) in setValuesFromTransaction, setValuesFromScope, and fromEvent, putting sentry-org_id= into outgoing baggage headers. In strict mode this silently breaks trace continuation because the SDK effectively has no org ID to match against incoming traces.



Summary
o123.ingest.sentry.io→"123")strictTraceContinuation(boolean, default false) andorgId(String) options toSentryOptionssentry-org_idin baggage / Dynamic Sampling ContextDecision Matrix
Changes
Dsn.java— org ID extraction from host using^o(\d+)\.regexSentryOptions.java—strictTraceContinuation,orgId, andgetEffectiveOrgId()Baggage.java—ORG_IDDSCKey, getter/setter, population insetValuesFromTransaction/setValuesFromScope/fromEventPropagationContext.java—shouldContinueTrace()logic infromHeaders()Scopes.java— pass options tofromHeaders()Test plan
Closes #5128
🤖 Generated with Claude Code