Skip to content

ref: Migrate internal spanToJSON callers to spanToStreamedSpanJSON - #23237

Draft
Lms24 wants to merge 5 commits into
developfrom
lms/migrate-to-streamed-span-json
Draft

ref: Migrate internal spanToJSON callers to spanToStreamedSpanJSON#23237
Lms24 wants to merge 5 commits into
developfrom
lms/migrate-to-streamed-span-json

Conversation

@Lms24

@Lms24 Lms24 commented Aug 10, 2026

Copy link
Copy Markdown
Member

This PR is prework for #23238:

  • Moves every SDK-internal spanToJSON call to the already-exported spanToStreamedSpanJSON function.
  • op, origin and description become attribute lookups (sentry.op, sentry.origin) and name
  • timestamp becomes end_timestamp.

Three type/behavior changes are included because the migration depends on them:

  • StreamedSpanJSON.attributes is now always set (so we/users don't need optional chaining)
  • end_timestamp became optional since we depend on checking the end timestamp for un-ended spans
  • SerializedStreamedSpan re-requires end_timestamp with a start_timestamp fallback so spans on the wire always carry one.
  • applyOtelSpanData reads its status from the static representation, since the streamed shape only narrows to 'ok' | 'error'.

Minor additional chainges

  • RawAttributes is exported from @sentry/core for typing attribute bags
  • Redis cache and setHttpServerSpanRouteAttribute use @sentry/conventions constants instead of string literals
  • Fixes a stale mock in the Remix instrumentServer test that returned description where the code reads name, which silently dropped the method-prefix branch from coverage

Lms24 and others added 5 commits August 10, 2026 14:50
`spanToJSON` now returns the intermediate `StreamedSpanJSON` representation
instead of the legacy `SpanJSON`. The previous behaviour is still available as
`spanToStaticSpanJSON`, and the old `spanToStreamedSpanJSON` export is gone
(it *is* `spanToJSON` now).

Field mapping for call sites: `description` -> `name`, `data` -> `attributes`,
`timestamp` -> `end_timestamp`, and `op`/`origin` move into `attributes` under
`sentry.op` / `sentry.origin`. `status` is narrowed to `'ok' | 'error'`.

`end_timestamp` is optional on `StreamedSpanJSON` and only required on
`SerializedStreamedSpan`, so the guarantee we actually care about — every span
we *send* has an end timestamp — is enforced at the single serialization
boundary (`streamedSpanJsonToSerializedSpan`) rather than baked into every span
representation. That keeps `end_timestamp` usable as the "has this span ended?"
signal it already was, so the open-span checks in `browserTracingIntegration`,
`idleSpan` and the React Router instrumentation stay simple field reads.
Moves all SDK-internal consumers off the static `SpanJSON` shape and onto the
already-existing `spanToStreamedSpanJSON`, which returns `StreamedSpanJSON`.
This is the semantically interesting half of the `spanToJSON` swap: `op`,
`origin` and `description` become attribute lookups (`sentry.op`,
`sentry.origin`) and `name`, and `timestamp` becomes `end_timestamp`.

No public API changes — `spanToJSON` keeps returning `SpanJSON`, so this is
a non-breaking change that can land on its own. The rename that makes
`spanToJSON` return the streamed shape follows separately.

Also included, since the migration depends on them:

- `StreamedSpanJSON.attributes` is now always set, and `end_timestamp` is
  optional (only present once a span has ended). OTEL reports `[0, 0]` for
  unended spans, which is now normalized to `undefined`.
- `SerializedStreamedSpan` re-requires `end_timestamp`, falling back to
  `start_timestamp`, so spans on the wire always carry an end timestamp.
- `RawAttributes` is exported from `@sentry/core` for consumers that need to
  type attribute bags.
- `applyOtelSpanData` reads the status message from the static representation,
  since the streamed shape only narrows to `'ok' | 'error'`.
- Redis cache and `setHttpServerSpanRouteAttribute` use `@sentry/conventions`
  attribute constants instead of string literals.

Fixes a stale mock in the Remix `instrumentServer` test that returned
`description` where the code reads `name`, silently dropping the
method-prefix branch from coverage.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Lms24 Lms24 changed the title lms/migrate to streamed span json ref: Migrate internal spanToJSON callers to spanToStreamedSpanJSON Aug 10, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit f38f841. Configure here.

() => (window as unknown as WindowWithSpan).thirdWaitingSpan.description,
() => (window as unknown as WindowWithSpan).secondWaitingSpan.name,
);
const thirdWaitingSpanName = await page.evaluate(() => (window as unknown as WindowWithSpan).thirdWaitingSpan.name);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Async-spans test reads wrong fields

Medium Severity

The test now reads .name from the waiting spans, but subject.js still stores spanToJSON(...) results that expose .description. Those assertions will get undefined and fail.

Fix in Cursor Fix in Web

Triggered by project rule: PR Review Guidelines for Cursor Bot

Reviewed by Cursor Bugbot for commit f38f841. Configure here.

// a cancelled span is reported as `ok`, with the raw status kept as an attribute
expect(spanJsonAfter.status).toBe('ok');
expect(attributes_after['sentry.status.message']).toBeUndefined();
expect(attributes_after['sentry.cancellation_reason']).toBe('document.hidden');

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Background-tab test fixture mismatch

Medium Severity

The test types and asserts on StreamedSpanJSON fields like name and attributes, but window.getSpanJson() still returns Sentry.spanToJSON(...). Cancelled-status expectations also diverge from the legacy payload shape.

Fix in Cursor Fix in Web

Triggered by project rule: PR Review Guidelines for Cursor Bot

Reviewed by Cursor Bugbot for commit f38f841. Configure here.

@github-actions

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size % Change Change
@sentry/browser 30.36 kB +0.02% +4 B 🔺
@sentry/browser - with treeshaking flags 28.53 kB +0.04% +10 B 🔺
@sentry/browser - with treeshaking flags tracing without tracing 26.86 kB +0.03% +8 B 🔺
@sentry/browser (incl. Tracing) 48.64 kB +0.06% +25 B 🔺
@sentry/browser (incl. Tracing + Span Streaming) 48.65 kB +0.06% +29 B 🔺
@sentry/browser (incl. Tracing, Profiling) 53.48 kB +0.04% +19 B 🔺
@sentry/browser (incl. Tracing, Replay) 88.06 kB +0.03% +20 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 77.48 kB +0.03% +22 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas) 92.79 kB +0.03% +26 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback) 105.48 kB +0.03% +24 B 🔺
@sentry/browser (incl. Feedback) 47.69 kB +0.01% +1 B 🔺
@sentry/browser (incl. sendFeedback) 35.18 kB +0.02% +4 B 🔺
@sentry/browser (incl. FeedbackAsync) 40.34 kB +0.01% +3 B 🔺
@sentry/browser (incl. Metrics) 31.43 kB +0.02% +5 B 🔺
@sentry/browser (incl. Logs) 31.66 kB +0.03% +7 B 🔺
@sentry/browser (incl. Metrics & Logs) 32.35 kB +0.03% +8 B 🔺
@sentry/react 32.15 kB +0.03% +7 B 🔺
@sentry/react (incl. Tracing) 50.83 kB +0.03% +11 B 🔺
@sentry/vue 35.45 kB +0.01% +2 B 🔺
@sentry/vue (incl. Tracing) 50.6 kB +0.04% +16 B 🔺
@sentry/svelte 30.38 kB +0.02% +4 B 🔺
CDN Bundle 31.62 kB +0.05% +14 B 🔺
CDN Bundle (incl. Tracing) 48.95 kB +0.08% +39 B 🔺
CDN Bundle (incl. Logs, Metrics) 33.85 kB +0.04% +11 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics) 50.91 kB +0.06% +30 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) 74.39 kB +0.02% +11 B 🔺
CDN Bundle (incl. Tracing, Replay) 86.51 kB +0.03% +20 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 88.42 kB +0.03% +24 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) 92.23 kB +0.03% +22 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 94.21 kB +0.03% +22 B 🔺
CDN Bundle - uncompressed 94.01 kB +0.07% +62 B 🔺
CDN Bundle (incl. Tracing) - uncompressed 147.07 kB +0.1% +144 B 🔺
CDN Bundle (incl. Logs, Metrics) - uncompressed 100.48 kB +0.07% +62 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 152.92 kB +0.1% +144 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 229.42 kB +0.04% +72 B 🔺
CDN Bundle (incl. Tracing, Replay) - uncompressed 266.33 kB +0.06% +154 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 272.17 kB +0.06% +154 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 280.02 kB +0.06% +154 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 285.86 kB +0.06% +154 B 🔺
@sentry/nextjs (client) 53.4 kB +0.04% +20 B 🔺
@sentry/sveltekit (client) 49.05 kB +0.05% +22 B 🔺
@sentry/core/server 65.61 kB +0.03% +15 B 🔺
@sentry/core/browser 51.93 kB +0.03% +12 B 🔺
@sentry/node 118.47 kB +0.02% +14 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 0 B added added
@sentry/node - without tracing 82.58 kB +0.02% +14 B 🔺
@sentry/aws-serverless 91.95 kB +0.01% +9 B 🔺
@sentry/cloudflare (withSentry) - minified 214.73 kB +0.02% +31 B 🔺
@sentry/cloudflare (withSentry) 530.38 kB +0.03% +152 B 🔺

View base workflow run

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.

1 participant