@sophonz/session-recorder
v0.0.5
Published
Sophonz distribution OpenTelemetry-based session recording
Readme
@sophonz/session-recorder
Session replay for the browser, recorded with rrweb and shipped over OTLP as log records.
Captures the DOM and its mutations, chunks each rrweb event into OTLP logs, and exports them gzipped. The records carry the same session ID and resource attributes as your traces, so a replay lines up with the spans from the same session.
Part of the Sophonz OpenTelemetry suite.
Install
bun add @sophonz/session-recorder
# or
pnpm add @sophonz/session-recorder
# or
npm install @sophonz/session-recorder@sophonz/browser-sdk initializes this recorder for you unless you pass
disableReplay: true, and exposes the masking options below through its own
config. Install it directly only when you are building on @sophonz/otel-web or
the OpenTelemetry SDK yourself.
Usage
The OpenTelemetry web SDK must be initialized first — the recorder reads the
tracer provider's Resource to tag its logs, and refuses to start without one.
import Rum from '@sophonz/otel-web';
import SessionRecorder from '@sophonz/session-recorder';
Rum.init({ /* ... */ });
SessionRecorder.init({
url: 'https://in-otel.sophonz.com/v1/logs',
maskAllInputs: true,
maskTextSelector: '*',
});Pause and resume around a screen you would rather not capture:
SessionRecorder.stop();
// ... sensitive flow ...
SessionRecorder.resume(); // takes a fresh full snapshotAPI
The default export is a singleton. A second init() on an already-initialized
recorder is a no-op.
init(config)
Starts recording. Returns nothing; check inited to confirm it started.
It gives up, with a console error, when called outside a browser or before the web SDK is initialized. It also returns silently when the sampler has decided not to record the current session, so an unsampled session costs nothing.
stop()
Stops emitting events. The rrweb recorder stays attached, so this is cheap to toggle.
resume()
Resumes emitting and takes a full snapshot, so the replay stays correct across whatever changed while stopped.
deinit()
Detaches the rrweb recorder entirely. init() can be called again afterwards.
inited
Read-only boolean.
Configuration
RumRecorderConfig is every rrweb record option
plus:
| Option | Type | Default | Description |
| -------- | ------- | ------------------------------------------ | ------------------------------------ |
| url | string | https://in-otel.sophonz.com/v1/logs | OTLP logs endpoint |
| debug | boolean | false | Log every exported record to console |
| apiKey | string | - | Currently has no effect — see below |
Two rrweb options are defaulted differently than rrweb itself, so recording is private unless you opt out:
maskAllInputs—truemaskTextSelector—'*'(all text masked)
apiKey is accepted and used to build an authorization header, but the header
is not passed to the exporter (src/index.ts), so the value is discarded.
Authenticate at the collector instead until that is wired up.
What gets sent
Each rrweb event is JSON-stringified and encoded, then split into chunks of at most ~950KB, since a single OTLP record cannot carry an arbitrarily large body. Every chunk is one log record carrying:
| Attribute | Description |
| ---------------------- | ------------------------------------ |
| rr-web.event | Event sequence number |
| rr-web.offset | Log sequence number |
| rr-web.chunk | Chunk index within the event, 1-based |
| rr-web.total-chunks | Chunk count for the event |
Records batch for 5 seconds before export, and flush on page unload. Bodies are
gzipped and posted with Content-Encoding: gzip.
Limits
These exist to keep a single misbehaving page from flooding ingest:
- Recording length — a session stops recording 4 hours after it started.
- Session rollover — when the session ID changes, recording restarts with a full snapshot, unless the tab is hidden. A background tab that regenerates its session stays quiet rather than duplicating the foreground tab's replay.
- Mutation rate limiting — a node mutating excessively (usually an animation)
is throttled. A
console.warnspan is emitted, and a full snapshot is queued a second later to recover the end state.
Dependencies
@opentelemetry/api,@opentelemetry/core,@opentelemetry/resourcesrrweb,rrweb-snapshot,@rrweb/typesfflate— gzipjson-stringify-safe,shimmer,type-fest
License
See LICENSE in this package.
Part of sophonz-js.
