Skip to content

feat(browser): breadcrumb trail on errors, console forwarding to logs - #1151

Merged
Makisuo merged 4 commits into
feat/browser-sdk-web-vitalsfrom
feat/browser-sdk-breadcrumbs
Sep 29, 2026
Merged

Makisuo merged 4 commits into
feat/browser-sdk-web-vitalsfrom
feat/browser-sdk-breadcrumbs

Conversation

@Makisuo

@Makisuo Makisuo commented Sep 29, 2026 •

Copy link
Copy Markdown
Collaborator

Part 5 of the browser SDK stack. Based on the Web Vitals PR.

An error arrived with no record of what led to it unless the session happened to be sampled into replay, the only path that captured console output.

What changes

  • Breadcrumbs (breadcrumbs, default true): the last 50 clicks, inputs, navigations and console lines are kept in memory for every session. Nothing leaves the page until an error is recorded; then the trail is exported as OTel log records linked to the error's span:
    • clicks / inputs / navigations: maple.browser.breadcrumb events with maple.breadcrumb.type, maple.breadcrumb.target (short selector, never an input value) and url.full;
    • console lines: ordinary log records at the console call's severity, with maple.breadcrumb.type: "console".
      Each breadcrumb is sent once; the next error gets the trail since the last one.
  • logs.captureConsole (default none): console levels exported as logs as they happen (maple.log.source: "console"), instead of only as breadcrumbs.

How

  • Clicks, inputs and navigations are already captured for every session by the session event sink. It gains a small onSessionEvent listener hook, kept on globalThis like the sink itself so every bundled copy of @maple/browser-session sees the same listeners.
  • Console lines come from the SDK's own capture, which runs whether or not replay records. @maple/browser-session exports its bounded console formatter (./console) for it. In replay-sampled sessions the console is wrapped twice; the formatter is bounded, and one owner for both paths wasn't worth the coupling.
  • Everything lives in the deferred chunk; errors.ts reaches it through a hook, so the eager bundle is unchanged (moving it there saved ~0.9 kB eager). Collection starts a moment after init().

Testing

Browser tests through the real init() path: trail exported and linked to the error span; each crumb sent once and nothing without an error; breadcrumbs: false; captureConsole forwarding without duplicating as a crumb. packages/browser-session suite still green.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

An error arrived with no record of what led to it unless the session was
sampled into replay, which is the only path that captured console output.

- The last 50 clicks, inputs, navigations and console lines are kept in
  memory for every session. Nothing leaves the page until an error is
  recorded; then the trail is exported as OTel log records linked to the
  error's span (maple.browser.breadcrumb events, and console lines as
  ordinary logs at their severity). Each breadcrumb is sent once.
- logs.captureConsole exports chosen console levels as logs as they
  happen, instead of only as breadcrumbs.
- Clicks, inputs and navigations come from the session event sink, which
  gains a small onSessionEvent listener hook (on globalThis like the sink).
  Console lines come from the SDK's own capture, which runs whether or not
  replay records; @maple/browser-session exports its console capture for it.
- All of it lives in the deferred chunk. errors.ts reaches it through a
  hook, so the eager bundle is unchanged. breadcrumbs: false turns it off.
@coderabbitai

coderabbitai Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 58 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 4 included reviews currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: da283182-069c-42a7-9041-664b51ac5eec

📥 Commits

Reviewing files that changed from the base of the PR and between ab3f6b9 and 6b8ab64.

📒 Files selected for processing (15)
  • docs/browser-sdk.md
  • packages/browser-session/package.json
  • packages/browser-session/src/events/events-sink.ts
  • packages/browser-session/src/index.ts
  • packages/browser-session/src/replay/capture/console.test.ts
  • packages/browser-session/src/replay/capture/console.ts
  • packages/browser/README.md
  • packages/browser/src/breadcrumbs.browser.test.ts
  • packages/browser/src/config.ts
  • packages/browser/src/deferred/breadcrumbs.ts
  • packages/browser/src/deferred/index.ts
  • packages/browser/src/errors.ts
  • packages/browser/src/index.ts
  • packages/browser/src/navigation.test.ts
  • packages/browser/src/tracing.browser.test.ts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@maple-review-bot

maple-review-bot Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Maple review

Confidence 3/5 · needs attention
Breadcrumbs are default-on and now export click target text and console lines from every session; the one reviewed file I did not read is the breadcrumbs browser test diff, so I cannot vouch for what …
quality 90/100 · 1 warning · tests partial · risk medium

Warning

This review ended early; what follows is what it established.

Adds an in-memory breadcrumb trail (clicks, inputs, navigations, console lines) exported as OTel log records linked to the recorded error's span, plus a logs.captureConsole option that forwards chosen console levels as logs. The wiring is coherent, but console capture is now installed by two owners whose teardowns are uncoordinated with replay suspend/resume.

  • startBreadcrumbs keeps the last 50 crumbs; flushBreadcrumbs exports them on the error's span
  • logs.captureConsole forwards the listed levels as maple.log.source: "console" log records
  • onSessionEvent listener hook in @maple/browser-session feeds clicks, inputs and navigations to breadcrumbs
  • setErrorRecordedHook lets eager errors.ts reach the deferred chunk's breadcrumb flush

Findings

Warning · F1 · Second console wrapper loses the breadcrumb wrapper on replay suspend

correctness · packages/browser/src/deferred/breadcrumbs.ts:84-92

installConsoleCapture (packages/browser-session/src/replay/capture/console.ts:28-33) restores console[level] to the function it captured at install time, and nothing coordinates this install with the replay path's own install (packages/browser-session/src/replay/events.ts:34, session/replay-session.ts:82-99). When the replay chunk installs over this wrapper, hiding the tab suspends replay and restores console.* to the breadcrumb wrapper only if the breadcrumb wrapper was the inner one — otherwise replay's teardown puts the native method back and every console.* breadcrumb and logs.captureConsole line silently stops for the rest of the page. Give the console one owner, or make the teardown conditional so a wrapper installed on top survives.

Guard the restore in `installConsoleCapture`: keep the wrapper function and only restore when `console[level] === wrapper`, so a wrapper installed over it is not discarded (and a stale wrapper is never resurrected).
What was checked
  • Breadcrumb attributes are lowercase dotted maple.breadcrumb.* plus semconv url.full (deferred/breadcrumbs.ts:52,119-121)
  • Click text is already gated by maskAllText and blocked subtrees (capture/interactions.ts:20)
  • flushBreadcrumbs clears the trail, so a second error gets only crumbs since the first
Files not reviewed (1)

The review ended before it read these diffs, so nothing above vouches for them.

  • packages/browser/src/breadcrumbs.browser.test.ts
Copy all findings (1)
Findings from an automated review of commit 9b9bdd46248ed273ec0bd374910cb1e00a2799ab. Verify each one against the current code before changing anything, fix only those that still apply, and keep each fix to the lines it names.

---

F1 · Warning · correctness · packages/browser/src/deferred/breadcrumbs.ts:84-92
Second console wrapper loses the breadcrumb wrapper on replay suspend
`installConsoleCapture` (`packages/browser-session/src/replay/capture/console.ts:28-33`) restores `console[level]` to the function it captured at install time, and nothing coordinates this install with the replay path's own install (`packages/browser-session/src/replay/events.ts:34`, `session/replay-session.ts:82-99`). When the replay chunk installs over this wrapper, hiding the tab suspends replay and restores `console.*` to the breadcrumb wrapper only if the breadcrumb wrapper was the inner one — otherwise replay's teardown puts the native method back and every `console.*` breadcrumb and `logs.captureConsole` line silently stops for the rest of the page. Give the console one owner, or make the teardown conditional so a wrapper installed on top survives.
Suggested fix: Guard the restore in `installConsoleCapture`: keep the wrapper function and only restore when `console[level] === wrapper`, so a wrapper installed over it is not discarded (and a stale wrapper is never resurrected).

9b9bdd4 · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple to ask about one.

@maple-review-bot maple-review-bot 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.

1 inline note from Maple's review. The score and summary are in the review comment above.

Comment thread packages/browser/src/deferred/breadcrumbs.ts
…eardown

Breadcrumbs and replay both wrap console.*. Tearing one down restored the
method it had captured at install, so replay suspending on a hidden tab
could remove the breadcrumb wrapper for the rest of the page. Teardown now
only restores when its own wrapper is still the installed one, and a torn
down wrapper left inside another's chain just forwards.
@maple-review-bot

maple-review-bot Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Maple review

Confidence 4/5 · likely safe to merge
Trail collection is bounded at 50 crumbs, input values are never captured, and the only teardown-sensitive code is the stacked console wrapper, which the new tests cover in both orders.
quality 100/100 · no findings · tests covered · risk medium · 3/3 new units observable

Adds an in-memory breadcrumb trail (clicks, inputs, navigations, console lines) exported as OTel logs linked to the error's span on captureException, plus logs.captureConsole for forwarding console levels live. The changes are bounded, gated by config, and safe to merge.

  • startBreadcrumbs/flushBreadcrumbs hold 50 crumbs and export them on error
  • logs.captureConsole emits chosen console levels as OTel logs immediately
  • onSessionEvent publishes sink events on globalThis for other bundled copies
  • installConsoleCapture teardown restores only its own wrapper and goes inert when stacked
What was checked
  • Console crumbs come from the SDK's own capture, not the sink, so ev.type === "console" is filtered out and lines are not duplicated (breadcrumbs.ts:73)
  • Input events carry only targetSelector; targetText is omitted under the default maskAllText (capture/interactions.ts:20), so no input value reaches a log body
  • Crumb buffer is bounded (50, newest kept) and cleared on teardown and on flush (breadcrumbs.ts:35, :93)
Observability coverage: 3 of 3 changes observable
Change Kind Observable Evidence
Error breadcrumb trail export log records linked to the error's span context yes breadcrumbs.ts:102-124 emits via emitLog with the error span's SpanContext; breadcrumbs.browser.test.ts:87 asserts the span id matches the exception span
Console capture to OTLP logs log records with maple.log.source: console yes breadcrumbs.ts:45-55 emitConsole; breadcrumbs.browser.test.ts:123
Session event listener hook in-page observer on the existing sink yes events-sink.ts:167-172 notifies listeners after the row is buffered; events feed the trail only

68493b1 · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple to ask about one.

@Makisuo
Makisuo added this pull request to stack #1161 September 29, 2026 21:22
@maple-review-bot

maple-review-bot Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Maple review

Confidence 4/5 · likely safe to merge
The two files the kickoff lists as changed since the last review are not in this head's diff, so no new code was added to review.
quality 100/100 · no findings · tests covered · risk medium · 2/2 new units observable

Adds an in-memory breadcrumb trail (clicks, inputs, navigations, console) exported as OTel logs linked to an error's span, plus logs.captureConsole forwarding. The head carries no code newer than the previous review; nothing new to object to.

  • startBreadcrumbs keeps 50 crumbs and flushBreadcrumbs exports them on error
  • onSessionEvent listener hook added to the browser-session sink
  • logs.captureConsole forwards chosen console levels as logs
  • installConsoleCapture teardown now only undoes its own wrapper
What was checked
  • Input breadcrumbs carry only targetSelector, no value (capture/interactions.ts:24-29)
  • Crumbs are cleared in flushBreadcrumbs and in stop, so none is exported twice or without an error (deferred/breadcrumbs.ts:93-105)
  • Clicks honour maskAllText and blocked subtrees, and url.full goes through scrubUrl (deferred/breadcrumbs.ts:121)
Observability coverage: 2 of 2 changes observable
Change Kind Observable Evidence
Breadcrumb trail export as OTel log records outbound telemetry yes emitLog with explicit spanContext links each record to the error span (deferred/breadcrumbs.ts:102-124)
Console forwarding as log records outbound telemetry yes emitConsole sets maple.log.source and the active span context (deferred/breadcrumbs.ts:44-55)

6b8ab64 · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple to ask about one.

@Makisuo
Makisuo merged commit 718abc5 into main Sep 29, 2026
38 checks passed
@Makisuo
Makisuo deleted the feat/browser-sdk-breadcrumbs branch September 29, 2026 21:44
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