Skip to content

feat: add sessionElapsed to scenario records - #20

Open
fateeand wants to merge 5 commits into
mainfrom
19-scenario-records-add-sessionelapsed-ms-since-the-rum-session-started
Open

fateeand wants to merge 5 commits into
mainfrom
19-scenario-records-add-sessionelapsed-ms-since-the-rum-session-started

Conversation

@fateeand

@fateeand fateeand commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

Overview

Scenario records get an optional sessionElapsed field: milliseconds since the RUM session started, which survives reloads. The RUM client keeps no start time, so the RUM sink records it and the scenario reads it back.

Key Changes

  • Recording the start (UwtRumTelemetrySink):
    • the metadata hook sees the client's own session_start event and records the moment, which covers a session starting after 30 idle minutes or on sign-out;
    • the client can also start the first session inside its constructor, before the hook exists. The sink then notices the session cookie (cwr_s, or cwr_s_<id> with unique) appear and records that moment instead. It only checks that the cookie exists, never its contents;
    • nothing is recorded while cookies are off, because the session ends with the page;
    • a guard stops the recursion that a sessionLengthSeconds of 0 would cause.
  • Storage (uwt-session-start.util): one localStorage entry, ngx-ui-watchtower.sessionStarts, holding the five most recent session ids with their start times. Five, because tabs can drift onto different sessions. It never throws: blocked storage, corrupt values and non-finite times are all handled.
  • Reading it (UwtScenario): toRecord() looks up the record's own sessionId and sets sessionElapsed, or leaves it out.
  • Fragments: a fragment gets the shell's session id through the identity handshake and finds its start in the shared localStorage (a Web Fragments iframe is same-origin and unsandboxed), so fragment and shell records sit on the same timeline.
  • Public API: uwtMarkSessionStart(sessionId), so a custom destination that owns its sessions can record starts too. The reader stays internal.

When the field is absent

The field is left out rather than guessed when:

  • there's no session yet, or no destination that records starts (the noop sink, an older shell version);
  • suppressSessionStartEvent is on, cookies are off, or localStorage is blocked;
  • the session began on a page in pagesToExclude, or before this version was running;
  • two tabs start sessions at the same instant and one overwrites the other's entry.

Impact on consumers

  • No change needed. The field is optional and elapsed keeps its meaning.
  • Micro-frontends: the shell has to run this version with the RUM sink; web fragments need nothing.

Closes #19

@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Coverage report

St.❔
Category Percentage Covered / Total
🟢 Statements
97.11% (+0.13% 🔼)
1278/1316
🟢 Branches
95.26% (-0.14% 🔻)
703/738
🟢 Functions
96.95% (+0.12% 🔼)
318/328
🟢 Lines
97.47% (+0.12% 🔼)
1235/1267
Show new covered files 🐣
St.❔
File Statements Branches Functions Lines
🟢
... / uwt-session-start.util.ts
100% 95.65% 100% 100%
Show files with reduced coverage 🔻
St.❔
File Statements Branches Functions Lines
🟢
... / uwt-rum-telemetry.sink.ts
95.83% (+0.6% 🔼)
91.96% (-0.59% 🔻)
93.88% (+0.85% 🔼)
97.31% (+0.42% 🔼)

Test suite run success

740 tests passing in 24 suites.

Report generated by 🧪jest coverage report action from f75274c

Copilot AI 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.

🟡 Changes recommended

The RUM sink can record a session start despite suppressSessionStartEvent, contradicting the documented behavior.

1 open issue
What changed in this PR

Adds optional sessionElapsed timing to scenario records by persisting RUM session start times across reloads and fragments while preserving the existing elapsed field.

Changes:

  • Adds resilient localStorage-backed session-start tracking and exports uwtMarkSessionStart.
  • Populates sessionElapsed from the record’s session ID.
  • Integrates session detection into the RUM sink and adds tests and documentation.
  • One moderate issue remains: the initial cookie-detection fallback does not honor suppressSessionStartEvent.
File Description
projects/​ngx-ui-watchtower/​src/​public-api.ts Exposes the custom session-start marker API.
projects/​ngx-ui-watchtower/​src/​lib/​utils/​uwt-session-start.util/​uwt-session-start.util.ts Stores recent session starts and calculates elapsed time.
projects/​ngx-ui-watchtower/​src/​lib/​utils/​uwt-session-start.util/​uwt-session-start.util.spec.ts Tests storage, retention, invalid data, and blocked storage.
projects/​ngx-ui-watchtower/​src/​lib/​sinks/​uwt-broadcast/​uwt-broadcast.spec.ts Verifies fragment records use the shell session timeline.
projects/​ngx-ui-watchtower/​src/​lib/​scenario/​uwt-scenario/​uwt-scenario.ts Adds sessionElapsed during record creation and renames duration state.
projects/​ngx-ui-watchtower/​src/​lib/​scenario/​uwt-scenario/​uwt-scenario.spec.ts Tests known and unknown session timing behavior.
projects/​ngx-ui-watchtower/​src/​lib/​models/​uwt-scenario.models/​uwt-scenario.models.ts Defines and documents the optional record field.
projects/​ngx-ui-watchtower/​rum/​src/​lib/​uwt-rum-telemetry.sink/​uwt-rum-telemetry.sink.ts Detects RUM session starts and persists their timestamps.
projects/​ngx-ui-watchtower/​rum/​src/​lib/​uwt-rum-telemetry.sink/​uwt-rum-telemetry.sink.spec.ts Tests RUM session-start detection and cookie behavior.
projects/​ngx-ui-watchtower/​DESIGN.md Documents session timeline semantics and storage behavior.

Review effort: Lite

Copilot AI 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.

🟢 Approval recommended

The remaining unique-cookie-path test coverage note is a non-blocking nit.

No open issues 🎉

1 resolved since last review

Review effort: Lite

return undefined;
}
const startedAt = readStored()?.find(([id]) => id === sessionId)?.[1];
return startedAt === undefined ? undefined : now - startedAt;

@TerranceKhumalo-absa TerranceKhumalo-absa Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should we return undefined when now - startedAt is negative, for example after a system-clock adjustment?

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.

Scenario records: add sessionElapsed (ms since the RUM session started)

3 participants