Skip to content

fix: transactions continue the external propagation context's trace - #5825

Draft
bitsandfoxes wants to merge 1 commit into
mainfrom
fix/external-propagation-context-transactions
Draft

bitsandfoxes wants to merge 1 commit into
mainfrom
fix/external-propagation-context-transactions

Conversation

@bitsandfoxes

Copy link
Copy Markdown
Contributor

ExternalPropagationContext has only been used with UseOtlp, which also sets DisableSentryTracing. So the hub never had an external trace and Sentry transactions at the same time. sentry-unity now has both: with Unity as a Library (getsentry/sentry-unity#2885), the game continues the trace of the native app's SDK (sentry-cocoa, sentry-java). Its events, logs and outgoing requests already join that trace through the external context. Its transactions don't: StartTransaction always starts a new trace. sentry-unity works around this in its own SentrySdk.StartTransaction. This draft proposes doing it in the hub instead. It's meant as a starting point for discussion.

  • StartTransaction treats the external context as the ambient parent when the transaction context has no parent of its own. With a sampling decision, the transaction becomes a child of the external span and inherits its decision and DSC. Without one, it's a root in the external trace and samples itself.
  • IExternalPropagationContext.IsSampled is now bool?. A native trace without an active span has no decision, and false made outgoing requests send sentry-trace: …-0 and sentry-sampled=false, so downstream services dropped their part. OtelPropagationContext returns null only without an Activity, where it has no trace ID either, so OTLP behaves as before.

Open questions:

  • There's no way to start a fresh root on purpose while the external context has a trace. new TransactionContext(name, op) always gets a random trace ID, so "no parent" is the only signal, and a trace ID passed explicitly without a parent is replaced. That would need an explicit opt-out, or a record of whether the trace ID was generated.
  • Events and headers prefer the external context over an active Sentry span (CaptureEvent_ExternalPropagationContext_PrefersExternalContext). With transactions in the external trace, an event inside one gets the right trace but the external span instead of the transaction's. sentry-unity hides the external trace while a sampled span of its own is active. Should an active Sentry span win when both exist? That's unchanged here because the order was deliberate for OTLP.

#skip-changelog: IExternalPropagationContext is internal and only sentry-unity uses it with Sentry tracing on.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.71429% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 76.13%. Comparing base (29bf3a4) to head (c24ec9b).

Files with missing lines Patch % Lines
...y.OpenTelemetry.Exporter/OtelPropagationContext.cs 0.00% 0 Missing and 1 partial ⚠️
src/Sentry/Internal/Hub.cs 92.30% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main    #5825   +/-   ##
=======================================
  Coverage   76.13%   76.13%           
=======================================
  Files         528      528           
  Lines       19579    19592   +13     
  Branches     3829     3834    +5     
=======================================
+ Hits        14906    14916   +10     
- Misses       3791     3792    +1     
- Partials      882      884    +2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@jamescrosswell

Copy link
Copy Markdown
Collaborator
  • There's no way to start a fresh root on purpose while the external context has a trace. new TransactionContext(name, op) always gets a random trace ID, so "no parent" is the only signal, and a trace ID passed explicitly without a parent is replaced. That would need an explicit opt-out, or a record of whether the trace ID was generated.

Would an alternative (to this PR) be that Unity calls ContinueTrace before calling StartTransaction... to continue the native trace?

And calling StartTransaction without calling ContinueTrace would start a new fresh root... does that work?

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants