Skip to content

✨ Add tab.id to all RUM and Logs events#4184

Open
mormubis wants to merge 11 commits intomainfrom
adlrb/context-tab-id
Open

✨ Add tab.id to all RUM and Logs events#4184
mormubis wants to merge 11 commits intomainfrom
adlrb/context-tab-id

Conversation

@mormubis
Copy link
Contributor

@mormubis mormubis commented Feb 13, 2026

Motivation

The Browser SDK shares session state across tabs via cookies/localStorage, but has no way to identify which specific tab produced a given event. Adding tab.id enables distinguishing events from different tabs within the same session, enabling tab-level correlation between RUM and Logs.

Changes

  • New tabContext module (packages/core/src/domain/contexts/tabContext.ts): Generates a UUID v4 per browser tab, persisted in sessionStorage so it survives same-tab navigations and reloads. Falls back to an in-memory UUID (cached at module level) when sessionStorage is unavailable (Web Workers, sandboxed iframes, quota exceeded), ensuring RUM and Logs always share the same tab.id within a page lifecycle.
  • Wired into RUM and Logs: Both SDKs call startTabContext(hooks) which registers an Assemble hook contributing { tab: { id: "<uuid>" } } to every event.
  • Unit tests: Hook output, ID consistency across triggers, sessionStorage persistence/reuse, fallback sharing across SDK instances, and error paths (getItem throws, setItem throws).

Field placement

tab.id sits at the top level alongside view, session, usr, etc. — it has a different lifecycle than a view (a single tab spans multiple views via SPA navigation) and is customer-facing, unlike the _dd internal namespace.

Note: A schema PR in rum-events-format is required to formally define tab in the event schema. That is tracked separately; the field is emitted correctly today via the open-ended index signatures in the generated type files.

Behavior

Scenario Behavior
New tab Generates UUID, persists to sessionStorage
Page reload / same-tab navigation Reuses UUID from sessionStorage
sessionStorage unavailable Shared in-memory UUID for the page lifecycle
Tab duplication Both tabs share the same ID (known tradeoff — inherent to sessionStorage-based schemes)

Test instructions

yarn test:unit --spec packages/core/src/domain/contexts/tabContext.spec.ts
yarn test:unit --spec packages/rum-core/src/domain/assembly.spec.ts
yarn test:unit --spec packages/logs/src/boot/startLogs.spec.ts
yarn typecheck

Manual verification (sandbox):

  1. Run yarn dev → open http://localhost:8080 in two separate tabs
  2. In each tab's DevTools console, paste:
    window.__ddBrowserSdkExtensionCallback = (msg) => {
      if (msg.type === 'rum' || msg.type === 'logs')
        console.log(msg.type, msg.payload.type ?? 'log', '→ tab.id:', msg.payload.tab?.id)
    }
  3. Observe: each tab emits a different UUID; reloading a tab preserves its UUID.

Checklist

  • Tested locally
  • Tested on staging
  • Added unit tests for this change.
  • Added e2e/integration tests for this change.
  • Updated documentation and/or relevant AGENTS.md file

@github-actions
Copy link

github-actions bot commented Feb 13, 2026

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@mormubis mormubis force-pushed the adlrb/context-tab-id branch from 8698d86 to 7aff300 Compare February 18, 2026 10:24
@cit-pr-commenter-54b7da
Copy link

cit-pr-commenter-54b7da bot commented Feb 18, 2026

Bundles Sizes Evolution

📦 Bundle Name Base Size Local Size 𝚫 𝚫% Status
Rum 173.86 KiB 174.12 KiB +273 B +0.15%
Rum Profiler 4.71 KiB 4.71 KiB 0 B 0.00%
Rum Recorder 24.88 KiB 24.88 KiB 0 B 0.00%
Logs 56.32 KiB 56.58 KiB +271 B +0.47%
Flagging 944 B 944 B 0 B 0.00%
Rum Slim 129.58 KiB 129.85 KiB +271 B +0.20%
Worker 23.63 KiB 23.63 KiB 0 B 0.00%
🚀 CPU Performance
Action Name Base CPU Time (ms) Local CPU Time (ms) 𝚫%
RUM - add global context 0.0055 0.0038 -30.91%
RUM - add action 0.022 0.0121 -45.00%
RUM - add error 0.0202 0.0117 -42.08%
RUM - add timing 0.0038 0.0025 -34.21%
RUM - start view 0.0157 0.0113 -28.03%
RUM - start/stop session replay recording 0.0008 0.0007 -12.50%
Logs - log message 0.0282 0.0141 -50.00%
🧠 Memory Performance
Action Name Base Memory Consumption Local Memory Consumption 𝚫
RUM - add global context 26.69 KiB 27.72 KiB +1.03 KiB
RUM - add action 49.54 KiB 51.45 KiB +1.91 KiB
RUM - add timing 26.97 KiB 25.80 KiB -1.17 KiB
RUM - add error 53.05 KiB 56.57 KiB +3.52 KiB
RUM - start/stop session replay recording 25.24 KiB 25.03 KiB -208 B
RUM - start view 448.38 KiB 459.28 KiB +10.90 KiB
Logs - log message 44.46 KiB 45.06 KiB +618 B

🔗 RealWorld

@datadog-datadog-prod-us1
Copy link

datadog-datadog-prod-us1 bot commented Feb 18, 2026

✅ Tests

🎉 All green!

❄️ No new flaky tests detected
🧪 All tests passed

🎯 Code Coverage (details)
Patch Coverage: 82.35%
Overall Coverage: 77.15% (-0.00%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 92c8de9 | Docs | Datadog PR Page | Was this helpful? React with 👍/👎 or give us feedback!

mormubis and others added 6 commits February 26, 2026 15:03
Introduce tabContext that generates a UUID v4 per browser tab, persisted
in sessionStorage to survive reloads, with an in-memory fallback when
storage is unavailable.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add _dd.browser_tab_id UUID field to the RUM common schema and the
Logs event types.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Register tabContext in both RUM and Logs event assembly so every
event includes _dd.browser_tab_id.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Make retrieveOrCreateTabId internal (not exported)
- Rewrite tabContext tests to use public startTabContext API
- Add edge case test for sessionStorage.setItem throwing
- Add RUM integration test for browser_tab_id in assembly
- Remove unrelated INP sub_parts from rumEvent.types.ts
- Add TODO comment for browser_tab_id schema sync

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The _dd object already has [k: string]: unknown, so the explicit
field is unnecessary. The proper type definition belongs in the
upstream rum-events-format schema.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Revert submodule and generated type changes that were unrelated to
the tab context feature. The browser_tab_id field flows through
_dd's [k: string]: unknown index signature and does not need
explicit type definitions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@mormubis mormubis force-pushed the adlrb/context-tab-id branch from 045cb13 to e53f2d3 Compare February 26, 2026 14:11
@mormubis mormubis marked this pull request as ready for review February 27, 2026 14:19
@mormubis mormubis requested a review from a team as a code owner February 27, 2026 14:19
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e53f2d3285

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

return newId
} catch {
// sessionStorage unavailable (e.g. Web Worker, sandboxed iframe, quota exceeded)
return generateUUID()

Choose a reason for hiding this comment

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

P1 Badge Reuse fallback tab ID across SDK initializations

When sessionStorage is unavailable, retrieveOrCreateTabId() returns a fresh UUID on every call. Since both startRumEventCollection() and startLogs() call startTabContext(), pages that initialize both SDKs in environments where storage throws (for example sandboxed iframes or restricted privacy modes) will emit different _dd.browser_tab_id values for RUM vs Logs from the same tab, breaking tab-level correlation. The fallback path should cache one in-memory ID per page lifecycle instead of regenerating it per initialization.

Useful? React with 👍 / 👎.

readonly presentation_delay: number
[k: string]: unknown
}
[k: string]: unknown

Choose a reason for hiding this comment

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

P2 Badge Keep INP sub_parts in exported RUM event typings

This deletion removes view.performance.inp.sub_parts from the public RumEvent type, but INP sub-parts are still produced at runtime in viewCollection (sub_parts is assigned when INP subparts exist). That creates a TypeScript API regression: consumers that previously read inp.sub_parts now get type errors even though the SDK still sends the field. The generated type should remain aligned with emitted payloads (including the correct processing_duration key).

Useful? React with 👍 / 👎.

Copy link
Member

Choose a reason for hiding this comment

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

The new field should be added in rum-events-format

@mormubis mormubis changed the title ✨ Add browser tab ID to all RUM and Logs events ✨ Add tab.id to all RUM and Logs events Mar 3, 2026
@mormubis mormubis requested a review from BenoitZugmeyer March 3, 2026 13:46
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.

2 participants