Skip to content

feat(browser): request/response headers on spans, replay bodies and canvas - #1157

Merged
Makisuo merged 8 commits into
feat/browser-sdk-offline-queuefrom
feat/browser-sdk-network-canvas
Sep 29, 2026
Merged

Makisuo merged 8 commits into
feat/browser-sdk-offline-queuefrom
feat/browser-sdk-network-canvas

fix(browser): cap replay bodies at what ingest keeps, match global re…

a38cd6e
Select commit
Loading
Failed to load commit list.
Maple Review Bot / Maple / review completed Sep 29, 2026 in 3m 15s

Confidence 3/5 · 1 issue to address

Confidence 3/5 · needs attention
The body-capture path changes when a replay network event is emitted; everything else is additive and covered by tests.
quality 90/100 · 1 warning · tests covered · risk medium · 1/2 new units observable

Adds allowlisted fetch/XHR headers as HTTP semconv span attributes, optional text bodies on replay network events, and opt-in rrweb canvas capture. Safe to merge apart from the deferred network event on streamed responses.

  • tracing.captureHeaders sets http.request.header.*/http.response.header.* on fetch and XHR spans
  • replay.networkBodies keeps cut text bodies on matched replay network events
  • replay.canvasFps records WebP canvas frames in both recorders
  • networkBodies.maxLength defaults to and is capped at 1,000

Findings

Warning · F6 · Fetch network event is deferred to the body read, so a streamed response records none

correctness · packages/browser-session/src/replay/capture/network.ts:61-78

When networkBodies matches the URL, record runs only inside the .then after readPrefix, which loops until the accumulated text exceeds maxLength (1,000 by default) and awaits reader.read() in between (network.ts:157). A text/event-stream or long-poll response that stays open without delivering 1,000 characters never resolves the read, so the request never appears in the replay network panel at all — before this change the event was recorded as soon as the fetch settled. TEXT_CONTENT matches text/event-stream because of its text/ prefix.

Emit the network event as soon as the response settles and attach the bodies only to the read that has finished, or exclude `text/event-stream` from `TEXT_CONTENT` so streams take the plain path.

Fixed since the last review

  • F5 · A g RegExp in networkBodies.urls matches only every other request
What was checked
  • F5 fixed: matchesUrl resets lastIndex before pattern.test (network.ts:12), global regex matches every request
  • Credential headers removed and names lowercased at config time (http-headers.ts:17), never reaching a span
  • Body reads are bounded by maxLength and the clone reader is cancelled (network.ts:157-163)
Observability coverage: 1 of 2 changes observable
Change Kind Observable Evidence
fetch/XHR span header attributes outbound call yes http.request.header.* / http.response.header.* set on the existing instrumentation spans (tracing.ts:267-290)
replay request/response body capture background read in the browser no bodies ride on session events as request.body/response.body (network.ts:35-36); no span is expected in a replay engine

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

Annotations

Check warning on line 78 in packages/browser-session/src/replay/capture/network.ts

See this annotation in the file changed.

@maple-review-bot maple-review-bot / Maple / review

correctness: Fetch network event is deferred to the body read, so a streamed response records none

When `networkBodies` matches the URL, `record` runs only inside the `.then` after `readPrefix`, which loops until the accumulated text exceeds `maxLength` (1,000 by default) and awaits `reader.read()` in between (network.ts:157). A `text/event-stream` or long-poll response that stays open without delivering 1,000 characters never resolves the read, so the request never appears in the replay network panel at all — before this change the event was recorded as soon as the fetch settled. `TEXT_CONTENT` matches `text/event-stream` because of its `text/` prefix.