Skip to content

fix(core): Use wall clock for structured log timestamps#22585

Open
antonis wants to merge 1 commit into
developfrom
fix/log-timestamp-wall-clock
Open

fix(core): Use wall clock for structured log timestamps#22585
antonis wants to merge 1 commit into
developfrom
fix/log-timestamp-wall-clock

Conversation

@antonis

@antonis antonis commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Before submitting a pull request, please take a look at our
Contributing guidelines and verify:

  • If you've added code that should be tested, please add tests.
  • Ensure your code lints and the test suite passes (yarn lint) & (yarn test).
  • Link an issue if there is one related to your pull request. If no issue is linked, one will be auto-generated and linked.

Closes getsentry/sentry-react-native#6510


Structured logs (Sentry.logger.*) are stamped with a timestamp that can be wrong by a large, constant offset on platforms where the Performance API is not anchored to the UNIX epoch — notably React Native/Hermes, where performance.timeOrigin + performance.now() tracks device uptime rather than wall-clock time. In one report the offset was ~4.25 days, while error events (which use Date.now()) were correct.

Log timestamps are produced by timestampInSeconds(), which returns performance.timeOrigin + performance.now() without any wall-clock sanity check. This PR switches the log path to dateTimestampInSeconds() (i.e. Date.now()), so log timestamps use the wall clock and match error-event timestamps on every platform.

Sub-second ordering is unaffected: logs already carry the sentry.timestamp.sequence attribute, which is the mechanism used to break ties when two logs share the same timestamp.

Scope: this is intentionally limited to the log path only. timestampInSeconds() and all of its other consumers (spans, sessions, breadcrumbs, metrics) are left unchanged, so tracing behavior is not affected.

Background

The regression was introduced in #16133 (ref(core): Switch to standardized log envelope), first shipped in @sentry/core@9.16.0. Before that change, logs were stamped with new Date().getTime() (wall clock); the refactor switched them to timestampInSeconds().

Related: #2590 (Performance-clock vs wall-clock skew).

How was it tested?

  • Added a unit test asserting that _INTERNAL_captureLog stamps logs with dateTimestampInSeconds() (wall clock) and does not call timestampInSeconds().
  • Updated the sentry.timestamp.sequence tests, which mocked timestampInSeconds(), to mock dateTimestampInSeconds() accordingly.
  • packages/core logs test suites pass (104 tests).

Structured logs stamped their timestamp via `timestampInSeconds()`, which
returns `performance.timeOrigin + performance.now()` with no wall-clock
sanity check. On platforms where the Performance API is not anchored to the
UNIX epoch (notably React Native/Hermes, where it tracks device uptime),
this made log timestamps off by a large, constant offset while error events
(which use `Date.now()`) stayed correct.

Switch the log path to `dateTimestampInSeconds()` so log timestamps use the
wall clock and match error-event timestamps on every platform. Sub-second
ordering is preserved via the existing `sentry.timestamp.sequence` attribute.

Scope is limited to the log path; `timestampInSeconds()` and its other
consumers (spans, sessions, breadcrumbs, metrics) are unchanged.

Closes getsentry/sentry-react-native#6510

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@antonis
antonis marked this pull request as ready for review July 24, 2026 09:47

@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 1 potential issue.

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 3aea457. Configure here.

// by a large, constant offset. Using the wall clock keeps log timestamps consistent with error-event timestamps
// (which also use `dateTimestampInSeconds()`). Sub-second ordering is preserved via the `sentry.timestamp.sequence`
// attribute below. See: https://github.com/getsentry/sentry-react-native/issues/6510
const timestamp = dateTimestampInSeconds();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shared sequence breaks across clocks

Medium Severity

Logs now call dateTimestampInSeconds() while metrics still call timestampInSeconds(), but both share one getSequenceAttribute counter keyed by millisecond. On divergent clocks (the React Native/Hermes case this fixes), an interleaved metric resets that counter, so same-ms logs can both get sentry.timestamp.sequence 0 and lose tie-break ordering.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 3aea457. Configure here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is valid but I'd wait for feedback from the team before expanding the scope of this change

@antonis
antonis requested a review from Lms24 July 24, 2026 09:54
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.

Structured **log** timestamps are wrong on React Native — logs use unchecked performance.timeOrigin + performance.now() instead of Date.now()

1 participant