Repository navigation
fix(opentelemetry): Normalize performance timestamps - #25744
MathurAditya724 wants to merge 2 commits into
Conversation
Preserve OpenTelemetry millisecond timestamps without changing direct Sentry span timing. Fixes #25743 Co-Authored-By: GPT-6 <codex@openai.com>
size-limit report 📦Compared: b359ca2 (PR test merge using develop at 84f5d12) Against: 84f5d12 (develop) · base run #63636 ✅ Baseline matches: this diff contains only this PR's changes.
|
Optional E2E testsSome optional checks were not successful. They do not block the merge. 4 not successful, 61 successful. See the run.
Successful checks
|
| @@ -38,6 +38,7 @@ import { | |||
| INTERNAL_setSegmentNameSourceIfSegment, | |||
| spanIsSegment, | |||
| spanTimeInputToSeconds, | |||
There was a problem hiding this comment.
h: instead of all of this, can we not just adjust spanTimeInputToSeconds to handle this case as well? that should already be called for started spans and when calling end, so this single point should be enough to tweak I believe?
There was a problem hiding this comment.
The tricky bit is that Sentry accepts numbers in seconds, while OTel uses milliseconds, including performance.now(). Those values can overlap, so guessing in the shared helper could break existing Sentry timestamps. We could consolidate the helpers with an explicit input mode, but would still need to distinguish the two.
|
I opened a PR with a slightly different (but similar) approach to solve this here: #25790 - thanks for bringing this up 🙏 |
#25790) **Why:** OpenTelemetry numeric time inputs are always milliseconds, either since the epoch or relative to the performance time origin (`performance.now()`). We passed them straight into `spanTimeInputToSeconds`, which treats numbers as seconds, so `performance.now()` start/end times produced 1970-dated spans with durations inflated 1000×. This can't be fixed by detecting the unit in `spanTimeInputToSeconds` itself: Sentry seconds (~1.8e9) and relative milliseconds overlap after ~21 days of process uptime. Within OTel semantics the split is unambiguous though — epoch milliseconds are > 1e12 for any date after 2001, while `performance.now()` needs ~30 years of uptime to get there. So `otelTimeInputToSeconds` applies OTel semantics, used by `SentryTracer` for `startTime` and by `SentrySpan` for `end`/`addEvent`/`updateStartTime` on spans marked via `spanIsTracerProviderSpan`. It's a separate function instead of a flag on `spanTimeInputToSeconds` so non-tracing bundles stay unchanged (+0 B on `@sentry/browser`, ~+70–80 B gzipped on tracing bundles). **How to review:** Start with `otelTimeInputToSeconds` in `spanUtils.ts`. Idle spans still cancel unfinished children with a seconds timestamp, which provider spans would read as relative milliseconds. I left that alone on purpose: idle spans only exist in the browser, where we don't really support OTel. **Unsure about:** `addEvent` keeps treating a falsy time as "now" (`startTime || undefined`), so `addEvent(name, attrs, 0)` on a provider span doesn't mean `performance.now()` zero. Kept for backwards compatibility. We don't really care about `addEvent` too much, so I'd argue it's fine to keep it and not think too much about this. Based on the work by @MathurAditya724 in #25744 — thank you! Closes #25743 Co-authored-by: Aditya Mathur <57684218+MathurAditya724@users.noreply.github.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
OpenTelemetry spans started or ended with
performance.now()now use Unix-epoch timestamps and preserve millisecond durations. Previously these numbers were interpreted as Sentry timestamps in seconds, causing Next.js client-component loading spans to appear near 1970 and inflating trace durations.Normalize numeric time inputs for OpenTelemetry-created spans while retaining the direct Sentry API's seconds-based behavior. Idle-span cancellation passes an unambiguous high-resolution timestamp so it works with either kind of descendant span.
Fixes #25743