Skip to content

feat: Send transactions as Span v2 streams#4912

Draft
jamescrosswell wants to merge 9 commits intomainfrom
span-first-envelopes
Draft

feat: Send transactions as Span v2 streams#4912
jamescrosswell wants to merge 9 commits intomainfrom
span-first-envelopes

Conversation

@jamescrosswell
Copy link
Collaborator

@jamescrosswell jamescrosswell commented Feb 11, 2026

Partially implements #4790:

Specifically it implements the first three steps of the Implementation Guidelines:

  1. Add the Span v2 Envelope (type), serialization logic and any utilities necessary to support sending a new envelope. See Span Protocol for more details.
  2. Add the top-level traceLifecycle (or trace_lifecycle) SDK init option which controls if traces should be sent as transactions or as spans (v2).
    • The allowed values for this option MUST be 'static' and 'stream'.
    • By default, the SDK MUST send traces as transactions ('static'). Span-First MUST be an opt-in feature.
    • Continue with adding Span-First logic which MUST only be applied if traceLifecycle is set to 'stream'.
  3. As an initial PoC, leave your current transaction APIs in place and convert the transaction event to a v2 spans array to be sent in the new envelope.
    • At this point, you can already start sending spans in batches (i.e. in multiple envelopes) to send more than 1000 spans at once. The maximum number of spans per envelope MUST be limited to 1000 and an envelope MUST only contain spans from one trace (as the trace envelope header is shared).

Converting classic/static transactions and spans to the SpanV2 format

To implement step 3, we have to be able to convert SentryTransaction and SentrySpan types to SpanV2 types so that these can be sent using the new streaming protocol, so we need to work out how to map the properties of these slightly different shapes to one another.

Some of these properties can be mapped to the base properties of the SpanV2 payload. Others have to be mapped to attributes, using the semantic conventions. We're mapping the properties of the Span Interface as follows:

Static Streaming
span_id ...
parent_span_id ...
trace_id ...
op [sentry.op]
description [sentry.description]
name name???
IsSampled [sentry.dsc.sampled???]
Sdk [sentry.sdk.*]
Environment [sentry.environment]
Platform ???
Release [sentry.release]
Distribution ???
User [user.*]
Request http.request.*
DynamicSamplingContext [sentry.dsc.*]
SampleRate [sentry.dsc.sample_rate]
Fingerprint N/A - arfitact of sending transactions as events
Contexts.App ???
Contexts.Browser Browser Attributes
Contexts.Device Device Attributes
Contexts.OperatingSystem Os Attributes
Contexts.Response http.response.*
Contexts.Runtime process.runtime.*
Contexts.Feedback N/A - arfitact of sending transactions as events
Contexts.Gpu ???
Contexts.Trace ??? - seems largely redundant
Breadcrumbs ???
start_timestamp ...
timestamp end_timestamp
status status (flattened)
origin [sentry.origin]
tags custom attributes ???
data custom attributes ???
measurements

Name

Classic spans just have a Description, so it seems logical to map Description to SpanV2.Name there.

Classic Transactions have both a Name and a Description.

It lookes like the Transaction.Name should probably map to sentry.segment.name since a Transaction in the old terminology is roughly equivalent to a Segment in the streaming protocol.

So for the span representing the Segment, should that also useTransaction.Name as the SpanV2.Name?

IsSampled

The IsSampled property of the classic span can be mapped to the sentry.dsc.sampled attribute, which is a boolean that indicates whether the span was sampled or not. I'm not sure if this is what other SDKs are doing.

Platform

The Platform property doesn't really have a natural home in the semantic convetions. It's always csharp. I guess we put this in a new custom attribute called sentry.platform? Would be good to align with other SDKs on this.

Distribution

Again, for distribution, the best I can find is to make up a new custom attribute called sentry.distribution. Should we do this?

Breadcrumbs

Will SpanV2 envelopes includ breadcrumbs?

Contexts

App

No idea what to do with the App context.

Runtime

Some of the fields from the runtime fit nicely in process.runtime.*. Build is about the only one that doesn't fit.

Gpu

No idea what to do with this. Custom attributes???

Status

Static spans/transactions allow for a multitude of quite specific Status codes. Streaming spans have just two: Ok and Error... so we obviously have to flatten this information.

Note

Possibly the specific error code could be preserved in an attribute???

Tags and Data

The static protocol allows for Tags and Data properties that store additional arbitraty information. The most obvious place to map these is to attributes.

It should be noted that it is possible to have collisions between the keys used for tags and data (for example if the SDK user has stored both a tag and a piece of data using the same key). It's also possible for collisions between the keys used for tags/data and the keys from our semantic conventions.

Note

Possibly we could prefix tags/data with tag. and data. to avoid collisions???

@github-actions
Copy link
Contributor

github-actions bot commented Feb 11, 2026

Semver Impact of This PR

None (no version bump detected)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


Features ✨

  • feat: Send transactions as Span v2 streams by jamescrosswell in #4912

Dependencies ⬆️

Deps

  • Apps built using the Sentry SDK for .NET must now target iOS version 15 or higher. Previously only version 13 or higher was required. (#4781) by github-actions in #4781
  • Bump Cocoa SDK from v8.57.3 to v9.2.0 (#4781) by github-actions in #4781
  • chore(deps): update Native SDK to v0.12.7 by github-actions in #4920

Other

  • test(android): Use volatile to produce SIGSEGV in native crash test by jpnurmi in #4919

🤖 This preview updates automatically when you update the PR.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 11, 2026

Fails
🚫 Please consider adding a changelog entry for the next release.
Messages
📖 Do not forget to update Sentry-docs with your feature once the pull request gets approved.

Instructions and example for changelog

Please add an entry to CHANGELOG.md to the "Unreleased" section. Make sure the entry includes this PR's number.

Example:

## Unreleased

### Features

- Send transactions as Span v2 streams ([#4912](https://github.com/getsentry/sentry-dotnet/pull/4912))

If none of the above apply, you can opt out of this check by adding #skip-changelog to the PR description or adding a skip-changelog label.

Generated by 🚫 dangerJS against aa8266d

@codecov
Copy link

codecov bot commented Feb 11, 2026

Codecov Report

❌ Patch coverage is 72.09302% with 24 lines in your changes missing coverage. Please review.
✅ Project coverage is 76.84%. Comparing base (f95f266) to head (fc0cebc).

Files with missing lines Patch % Lines
src/Sentry/Protocol/SpanV2.cs 68.49% 21 Missing and 2 partials ⚠️
src/Sentry/Protocol/Envelopes/Envelope.cs 83.33% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4912      +/-   ##
==========================================
+ Coverage   73.88%   76.84%   +2.96%     
==========================================
  Files         494      406      -88     
  Lines       17868    15156    -2712     
  Branches     3509     3025     -484     
==========================================
- Hits        13202    11647    -1555     
+ Misses       3807     2791    -1016     
+ Partials      859      718     -141     

☔ View full report in Codecov by Sentry.
📢 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 jamescrosswell mentioned this pull request Feb 17, 2026
8 tasks
@jamescrosswell jamescrosswell changed the title feat: Span v2 Envelopes feat: Send transactions as Span v2 streams Feb 17, 2026
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Flash0ver just a heads up, I needed Attributes for the span streaming stuff so extracted this functionality out from the SentryMetrics class... just in case you might change any of this in another PR.

@sentrivana
Copy link

Name

Classic spans just have a Description, so it seems logical to map Description to SpanV2.Name there.

Classic Transactions have both a Name and a Description.

It lookes like the Transaction.Name should probably map to sentry.segment.name since a Transaction in the old terminology is roughly equivalent to a Segment in the streaming protocol.

So for the span representing the Segment, should that also use Transaction.Name as the SpanV2.Name?

Yes to both.

IsSampled

The IsSampled property of the classic span can be mapped to the sentry.dsc.sampled attribute, which is a boolean that indicates whether the span was sampled or not. I'm not sure if this is what other SDKs are doing.

In Python, we store the sampled flag outside of attributes as a standalone property on the span object. It doesn't make sense to store it as an attribute IMO since there's no value in sending it over the wire (since it'd always be True; otherwise the span wouldn't have been sent in the first place).

Platform

The Platform property doesn't really have a natural home in the semantic convetions. It's always csharp. I guess we put this in a new custom attribute called sentry.platform? Would be good to align with other SDKs on this.

sentry.platform already exists and is the right place for this

Distribution

Again, for distribution, the best I can find is to make up a new custom attribute called sentry.distribution. Should we do this?

sentry.dist is a thing, though unsure what exactly it should contain. @buenaflor might know more here since it looks mobile specific

Breadcrumbs

Will SpanV2 envelopes includ breadcrumbs?

I believe not since the protocol doesn't take them into account, @cleptric?

App

No idea what to do with the App context.

Would these fit? If at least partially, we can extend them and add to our conventions.

Runtime

Some of the fields from the runtime fit nicely in process.runtime.*. Build is about the only one that doesn't fit.

I'd suggest to add it under process.runtime.build and add it to conventions.

Gpu

No idea what to do with this. Custom attributes???

Does OTel have anything for this? If yes, we should follow that (and add it to our conventions). If not, we should come up with something new (and add it to our conventions).

Status

Static spans/transactions allow for a multitude of quite specific Status codes. Streaming spans have just two: Ok and Error... so we obviously have to flatten this information.

Note

Possibly the specific error code could be preserved in an attribute???

Correct, V2 spans start with status ok and if there's a problem the status should be set to error. The specific reason should be set in an extra attribute if possible, e.g. http.response.status_code for HTTP responses. We also have error.type and the exception namespace.

Tags and Data

The static protocol allows for Tags and Data properties that store additional arbitraty information. The most obvious place to map these is to attributes.

It should be noted that it is possible to have collisions between the keys used for tags and data (for example if the SDK user has stored both a tag and a piece of data using the same key). It's also possible for collisions between the keys used for tags/data and the keys from our semantic conventions.

Note

Possibly we could prefix tags/data with tag. and data. to avoid collisions???

In general, for SDK-set tags/data, these should, if possible, be matched to corresponding attributes from the Sentry or OTel conventions. We shouldn't attempt to auto-translate user-set tags/data to attributes; the setAttribute API should be used for setting attributes, with user-set attributes having precedence over SDK-set ones.

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.

3 participants

Comments