Skip to content

Real User Monitoring

Osuite Real User Monitoring (RUM) instruments your frontend the same way the rest of Osuite instruments your backend: as OpenTelemetry spans, logs, and resource attributes. Browser activity arrives as OTLP traces and logs alongside your services, and every browser fetch propagates W3C traceparent and baggage headers so a user’s click stitches into the same trace as the backend services it triggered.

Alongside spans and logs, the RUM SDK records full session replay (powered by rrweb) and uploads it as gzipped chunks. Each replay is linked to its session, its user, and any errors that fired during the visit, so a slow click in production lands you on the exact session, the exact stack trace, and the exact backend trace it produced — in one view.

The browser SDK is published as @osuite/rum and works in any browser application. A separate React subpath, @osuite/rum/react, adds an error boundary without pulling React into the baseline bundle. Framework guides today cover React and Next.js.

The SDK runs once on page load and emits three streams:

StreamTransportWhat it carries
SpansOTLP /v1/tracesdocumentLoad, documentFetch, resourceFetch, HTTP {METHOD}, route_change, user_interaction.*
Log recordsOTLP /v1/logssession.start, session.end, user.changed, sampling.upgraded, browser.error, browser.resource_error, browser.unhandled_rejection, browser.react_error, browser.console_*, browser.manual, replay.chunk
Replay chunksS3 presigned PUTGzipped rrweb event blobs, plus a replay.chunk pointer log so they are queryable from the session log stream

Every span and log carries session.id, user.id? (when set), service.name, service.version, service.environment, and browser/connection metadata. That shared schema is what lets RUM data join your backend traces and logs without any extra correlation step.

Terminal window
npm install @osuite/rum
# or
pnpm add @osuite/rum

React is declared as an optional peer dependency, so if your app already uses React you can import the error boundary from @osuite/rum/react without any extra install. The baseline bundle does not pull React in.

import { osuite } from '@osuite/rum';
osuite.init({
apiKey: '<your-ingest-token>',
endpoint: 'https://ingest.<region>.osuite.io',
apiEndpoint: 'https://api.<region>.osuite.io',
service: {
name: 'frontend-web',
version: process.env.NEXT_PUBLIC_BUILD_SHA ?? 'dev',
environment: process.env.NEXT_PUBLIC_ENV ?? 'development',
},
});
osuite.setUser({ id: 'u_123', plan: 'pro' });
try {
await doWork();
} catch (err) {
osuite.captureException(err, { extra: { feature: 'checkout' } });
}

apiKey, endpoint, and apiEndpoint fall back to the OSUITE_INGEST_TOKEN, OSUITE_INGEST_ENDPOINT, and OSUITE_API_ENDPOINT environment variables when omitted. That call is enough to get traces (page loads, fetch/XHR, route changes, clicks), error logs, and full session replay flowing, with strict privacy masking on by default. See the SDK reference for every option.

The SDK injects W3C traceparent and baggage headers on outgoing fetch and XHR requests. The backend OTel SDK reads them, links its server span as a child of the browser span, and the resulting trace contains both halves of the request — frontend and backend — in one waterfall.

session.id and user.id ride along in baggage so backend logs and traces emitted during that request are also attributable to the originating session. From a backend error, you can pivot to the exact session that produced it; from a session, you can pivot to every backend trace it triggered.

By default propagation is same-origin only — the SDK refuses to send trace headers to third-party APIs, analytics, or ad pixels. You can extend it with an explicit allowlist (see Propagation).

rrweb captures the DOM, every interaction, scroll, and route change, stitched per session and linked to the spans, logs, and errors that occurred during the visit. Masking is strict by default: all text and inputs are hidden unless you opt in per element.

See Session replay for masking, privacy classes, and upload behavior.

The SDK listens on every uncaught error source by default — runtime errors, unhandled Promise rejections, failed resource loads, and (with the React boundary) render crashes. Errors group on stack-trace fingerprint, and every group links to the matching session replay and backend trace.

See Frontend errors for the error sources, event names, and manual capture.

Page load timing is captured from the Navigation Timing API as documentLoad, documentFetch, and resourceFetch spans (via the OpenTelemetry document-load instrumentation), and every fetch/XHR becomes an HTTP {METHOD} span — so you get real, per-session load and request latency rather than synthetic averages.

SPA route changes are captured as route_change spans that automatically adopt the fetch and XHR calls fired during navigation, so you see the true settling time of every page transition rather than a synthetic “navigation start” timestamp. The route is declared settled when the DOM is quiet for 500 ms (5 s hard cap).

Optional UX-friction signals — rage_click, dead_click, scroll_depth — are off by default and switch on per config flag (see Interactions).

Core Web Vitals (LCP, INP, CLS, FCP) are not emitted today. Use documentLoad timing attributes and HTTP {METHOD} span durations as the primary performance signals.

The default is sessionSampleRate: 1.0 — every session is sampled. As traffic grows you lower it, and with error-triggered upgrade the broken sessions are kept even when unsampled.

See Sampling for the upgrade-on-error safety net and the pre-sample buffer.

  • React — mount the SDK and wrap your tree in OsuiteErrorBoundary
  • Next.js — initialize in a client component and match source maps to your build
  • APM & Tracing — backend tracing that the RUM SDK propagates into via traceparent and baggage
  • Logs — RUM error logs and replay.chunk pointers land in the same Logs Explorer as your backend logs
  • Alerts — alert on frontend error rate or rage-click spikes