@oodle-ai/rum
v0.4.0
Published
Oodle Real User Monitoring SDK
Readme
@oodle-ai/rum
Real User Monitoring SDK for Oodle. Captures browser performance data, user sessions, errors, and session replay.
Install
npm install @oodle-ai/rumQuick start
import { OodleRum } from '@oodle-ai/rum';
OodleRum.init({
instanceId: '<OODLE_INSTANCE_ID>',
apiKey: '<OODLE_RUM_API_KEY>',
endpoint: 'https://<COLLECTOR_DOMAIN>',
service: 'my-app',
env: 'production',
version: '1.0.0',
sessionReplay: true,
sessionSampleRate: 100,
replaySampleRate: 100,
privacyLevel: 'mask-user-input',
});Enabling it per environment
Gate init() alone — the rest of the API is safe to call
unconditionally from shared code:
if (import.meta.env.PROD) {
OodleRum.init({ /* ... */ });
}
// No environment check needed here or at any other call site.
OodleRum.identify({ id: user.id });
OodleRum.trackEvent('checkout_completed');Without init(), nothing is recorded and nothing is sent:
trackEvent and flush do nothing, and getSessionId()
returns ''. identify, setTags, and addFeatureFlag
keep working, so state set before a later init() still
applies once it runs — a setTags call takes precedence
over the tags passed to init() on any key both set.
User identification
OodleRum.identify({
id: 'user-123',
name: 'Jane Doe',
email: '[email protected]',
});Custom events
OodleRum.trackEvent('checkout_completed', {
orderId: '12345',
total: 99.99,
});Configuration
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| instanceId | string | required | Oodle instance ID |
| apiKey | string | required | RUM API key (write-only, safe for client-side) |
| endpoint | string | required | Collector URL (must be HTTPS in production) |
| service | string | required | Application name |
| env | string | '' | Environment (e.g. production, staging) |
| version | string | '' | Application version |
| sessionReplay | boolean | true | Enable session replay recording |
| sessionSampleRate | number | 100 | Percentage of sessions to track (0-100) |
| replaySampleRate | number | 100 | Percentage of tracked sessions to record replay (0-100) |
| privacyLevel | string | 'mask-user-input' | 'mask-user-input' masks password/email inputs; 'mask' masks all text and inputs; 'allow' masks nothing |
| tags | object | undefined | Custom key-value tags attached to all events |
| viewUrlQueryParams | string[] | undefined | Query parameter names to keep in view_url on every event (for example ['traceId']). All other parameters are dropped. See Privacy |
What's collected
- Web Vitals — LCP, FID, CLS, INP, TTFB, FCP
- Page views and navigation timing
- JavaScript errors and unhandled rejections
- Network requests — fetch and XHR with timing
- User actions — clicks, rage clicks, dead clicks
- Console errors
- Session Replay — pixel-perfect DOM recordings
Privacy
The SDK removes the query string from view_url and referrer_url, because
a query string can hold tokens or personal data. Two other places keep the
full URL: resource_url on network request events, and the page URL that
session replay records. Keep secrets out of URLs, or turn off
sessionReplay for pages that have them in the URL.
To keep a parameter that only identifies page state, such as the ID of the
record a page shows, list its name in viewUrlQueryParams:
- Every event (views, actions, errors, resources, console) carries
view_url, so every event keeps the listed parameters.view_url_pathandreferrer_urlnever contain a query string. - The SDK keeps the value of a listed parameter as it is. Do not list a parameter that can hold personal data or a token.
view_urlgets one value for each record ID. To count or group views by page, useview_url_path.- Names match exactly and are case-sensitive.
- The kept parameters are encoded again, so
view_urlcan differ from the address bar (for example,%3Afor:and+for a space). Decode the value before you compare it with an ID. - Parameters after
#in hash-based routes are not kept. - A value that is not an array of strings is ignored, with a console warning.
The privacyLevel setting controls input masking in session replay:
'mask-user-input'(default) — masks password and email fields'mask'— masks all text content and all input types'allow'— masks nothing
Excluding specific elements
privacyLevel applies to the whole page. To exclude individual elements,
mark them in your markup. Both an attribute and a class are accepted for
each level, so you can use whichever fits your templates:
<!-- Not recorded at all. The element and its subtree are replaced by an
empty placeholder of the same size, so layout is preserved. -->
<div data-oodle-privacy="hidden">...</div>
<div class="oodle-privacy-hidden">...</div>
<!-- Recorded, but text is replaced with dots. Structure and styling are
kept, so the replay still shows the shape of the page. -->
<span data-oodle-privacy="mask">[email protected]</span>
<span class="oodle-privacy-mask">...</span>Use hidden when the element itself is sensitive, and mask when only its
contents are. hidden is also the way to keep a subtree that is very large
or mutates constantly (a canvas, a virtualized log view) out of the
recording — the cost of recording it is skipped along with the content.
Marking works in both directions relative to privacyLevel: an element
marked mask is masked even under 'allow', and unmarked elements still
follow whatever the global level says.
rrweb's own class names work too, since the SDK leaves them at their defaults:
.rr-block— same effect as.oodle-privacy-hidden.rr-mask— same effect as.oodle-privacy-mask.rr-ignore— records the input element, but never its typed value
Elements are matched live, so toggling a class at runtime takes effect from the next DOM mutation onward. Content recorded before the class was added is already in the replay, so apply the marker before the sensitive value is rendered rather than after.
License
Apache-2.0
