RUM Data Reference

Reference for RUM telemetry in Edge Delta: page load and route change spans, request spans, error logs, sessions, backend trace joining, and attributes.

Overview

The RUM SDK emits two signals. Views and requests are trace spans, while errors and messages are log records, so RUM data appears in two places and is sent at two different times:

SignalCarriesSent
View spansOne documentLoad span per page load and one routeChange span per single page application navigation, with Core Web VitalsWhen the visitor leaves, backgrounds the page, or the next view opens
Request spansOne client span per fetch or XHR request, when requestSpans is onBatched, within about 5 seconds
Error logsUncaught errors, unhandled promise rejections, handled errors from addError, and messages from addMessageBatched, within about 5 seconds

Errors are log records rather than spans because a browser error caught by a global handler has no span to attach an exception to. As logs, they also gain pattern grouping and full text search over messages and stacks in Log Search. The log drawer shows each error’s exception.* attributes along with the page and session context:

Log Search showing RUM JavaScript errors with exception attributes in the log drawer Log Search showing RUM JavaScript errors with exception attributes in the log drawer

Views and sessions

A view is one trace: the document load, then one per route change. Each view carries its own span, its own Cumulative Layout Shift (CLS), and its own Interaction to Next Paint (INP), and every error reported while the view is open carries its trace ID. Views are joined to each other by session.id, not by trace ID, so a 40-minute single page session is many traces, not one.

A session groups the views and errors of one visit:

  • A session ends after 15 minutes of inactivity and is capped at 4 hours.
  • The session ID is stored in the browser’s sessionStorage under the key ed_rum_session.
  • The sampling decision (sampleRate) is made once when the session starts and pinned for its lifetime, so sessions are never half-recorded.
  • When storage is unavailable, such as in some private browsing modes, sessions degrade to one per page view instead of persisting across pages.

Page load spans

Each page load produces one span named documentLoad, matching the OpenTelemetry document load instrumentation span name. The span starts at navigation start and ends at the load event, and it carries the load phase timings and Core Web Vitals as attributes.

The span is sent when the page is hidden (the visitor switches tabs, navigates away, or closes the page), not when the load finishes. CLS and INP keep changing while the visitor is on the page and are only final at that point. When testing, switch tabs or close the page before looking for the span.

By default, the load phases are attributes on the one documentLoad span, so query them as fields rather than expecting a waterfall. Setting navigationSpans: true in the SDK configuration emits the phases as child spans instead, so the trace draws as a waterfall:

documentLoad
├─ dnsLookup
├─ connect
│  └─ tls
├─ request
├─ response
├─ domProcessing
└─ loadEvent

The phases stay on the view span as attributes either way, and the attributes remain what you query. The child spans exist to be drawn and carry no attributes of their own. Phases that did not happen are skipped: a reused keep-alive connection reports no dnsLookup or connect span, and tls is absent over plain HTTP.

The option is off by default because it is a volume decision. Each span is a separate item downstream, so a page view goes from one trace item to as many as eight.

Route change spans

With routeChanges on (the default), each single page application navigation produces a span named routeChange and opens a new view. Only a change of path opens one; a router rewriting the query string on every filter change costs nothing.

The span covers the transition, by default from the URL change to the first paint after it. Its vitals cover the whole time the view was open, which is longer: a layout shift ten seconds in still belongs to the route the visitor was looking at. When the application names routes through endView or startView, the span carries the route pattern in ed.rum.route, for example /orgs/$orgId/logs rather than /orgs/42/logs, so spans group by route instead of by URL. Span names stay documentLoad and routeChange either way. See Views for wiring a router.

Request spans

With requestSpans: true, every fetch and XHR request the page makes produces one client span, named by method and host, for example GET api.acme.com. Request spans belong to the view’s trace, so a slow API call shows up inside the page view that made it.

AttributeDescription
http.request.methodThe request method.
url.fullOrigin and path of the request. Query strings and fragments are stripped.
http.response.status_codeThe response status. Absent when the request failed at the network layer.
ed.rum.routeThe route pattern of the view the request happened in, when known.

Three kinds of request are never reported: the SDK’s own ingestion requests (a span for an export would itself be exported, producing a loop), non-HTTP schemes such as blob: and data:, and URLs that do not parse.

requestSpans is off by default because it is the largest volume multiplier in the SDK: it adds one span for every HTTP request every recorded page makes. Turn it on deliberately, and consider sampleRate alongside it.

Error log records

Errors are exported as OTLP log records with severity ERROR. The record body is the error message, and the details are in attributes:

AttributeDescription
exception.typeThe error name, for example TypeError or UnhandledRejection.
exception.messageThe error message, truncated to 1024 characters.
exception.stacktraceThe stack trace when available, truncated to 8192 characters.
ed.rum.breadcrumbsA JSON array of the last 20 things that happened before the error, newest last. See Breadcrumbs.
ed.rum.mechanismWhich React path reported a render error: react.boundary, react.root, or react.router. See React error reporting.
ed.rum.error_idThe event ID a React error boundary can show to the user, so one quoted ID finds one exact error.

Uncaught errors and unhandled promise rejections are captured automatically while captureErrors is enabled. Errors you handle yourself can be reported with addError, optionally with a fingerprint that groups unstable messages, and addMessage sends a log record with no exception.* attributes at all. The loader also buffers up to 20 errors thrown before the SDK bundle finishes loading and reports them once it arrives.

Correlating errors with page loads

Everything one view emits shares one trace ID, and each error log record carries it. In the log drawer, use the Search in Traces action on the trace ID to open the view span for the page load or route change the error happened in. Errors thrown while the page was still loading also carry the span ID of the documentLoad span.

Log drawer showing a RUM error with its pipeline and trace ID Log drawer showing a RUM error with its pipeline and trace ID

session.id and the url.* attributes are on both signals as well, which is how you widen from one error to everything else that visitor did.

Connect browser traces to backend traces

With requestSpans: true, the SDK sends a W3C traceparent header on the page’s HTTP requests, so the span your server records becomes a child of the browser’s request span and one trace covers the click and the query behind it. Your backend needs no new code: reading traceparent is default behavior in every OpenTelemetry SDK and in common vendor agents.

TargetWhat it takes
Same-origin APINothing. No CORS is involved, so nothing can break.
Cross-origin APItraceparent in that server’s Access-Control-Allow-Headers, then the host in propagateTo.
The page load itselfA Server-Timing header on the document response.

For cross-origin APIs, deploy the CORS change before listing the host. traceparent is not a CORS-safelisted header, so adding it makes the request preflighted, and a server that does not allow the header rejects the preflight, which means the request never leaves the browser. This fails hard, not quietly.

A string entry in propagateTo matches the whole host, and there are no wildcards: an entry matching more than it names hands the application’s trace topology to a third party. For a family of hosts, pass a RegExp or a predicate, and anchor the pattern; unanchored, /acme\.com/ also matches evil-acme.com:

propagateTo: [/\.acme\.com$/, (host) => ourServices.has(host)]

The page load itself cannot propagate: no script is running when the browser asks for the HTML. Have that response return its own trace context instead, and the documentLoad span links to it:

Server-Timing: traceparent;desc="00-<trace-id>-<span-id>-01"

A cross-origin document response also needs Timing-Allow-Origin for the browser to expose the value at all.

Attribute reference

Resource attributes

These attributes describe the application and browser, and appear on both signals:

AttributeDescription
service.nameThe service value from the SDK configuration.
service.versionThe version value, when set.
deployment.environment.nameThe environment value, when set.
user_agent.originalThe browser’s user agent string, used for browser, OS, and device breakdowns.
browser.languageThe browser language, for example en-US.
browser.platformThe operating system platform. Reported by Chromium-based browsers only.
browser.mobileWhether the device reports itself as mobile. Reported by Chromium-based browsers only.
telemetry.sdk.name@edgedelta/browser-rum.
telemetry.sdk.versionThe SDK version.

View and session attributes

These attributes appear on view spans and on every error log record:

AttributeDescription
session.idGroups a visitor’s views and errors.
url.fullThe page origin and path. Query strings and fragments are stripped, since they often carry tokens and personal data.
url.pathThe page path.
url.domainThe page hostname.
url.schemehttp or https.
ed.rum.routeThe route pattern, when the router supplied one through startView or endView.
user.id, user.email, user.namePresent once setUser is called.

Attributes added with setContext appear alongside these on everything sent after the call.

Core Web Vitals attributes

Vitals are attributes on view spans. Each view measures its own vitals, so a route change’s CLS and INP describe that view, not the whole visit. Timing vitals are reported in milliseconds; CLS is unitless. Each vital has a matching .rating attribute (for example ed.rum.lcp.rating) with the value good, needs-improvement, or poor:

AttributeMetric
ed.rum.lcpLargest Contentful Paint (LCP).
ed.rum.clsCumulative Layout Shift (CLS).
ed.rum.inpInteraction to Next Paint (INP).
ed.rum.fcpFirst Contentful Paint (FCP).
ed.rum.ttfbTime to First Byte (TTFB).

A vital that could not be measured is absent. For example, a view the visitor never interacted with reports no INP.

Load phase and navigation attributes

Load phases are reported in milliseconds on the documentLoad span. A phase that did not happen is absent, so a reused keep-alive connection reports no ed.rum.dns_ms or ed.rum.tcp_ms:

AttributeDescription
ed.rum.navigation_typeHow the view was reached: navigate, reload, or back_forward.
ed.rum.dns_msDNS lookup duration.
ed.rum.tcp_msConnection establishment duration.
ed.rum.tls_msTLS handshake duration. Absent over plain HTTP.
ed.rum.request_msTime from request start to first response byte.
ed.rum.response_msResponse download duration.
ed.rum.dom_interactive_msTime from navigation start until the DOM became interactive.
ed.rum.dom_content_loaded_msTime from navigation start until DOMContentLoaded completed.
ed.rum.transfer_sizeBytes transferred for the document, over the wire.

Querying and monitoring

Views and requests are trace data, and errors are log data, so build queries and monitors accordingly:

  • In the Trace Explorer, filter by your service.name and the span names documentLoad and routeChange, then facet by url.path, ed.rum.route, ed.rum.lcp.rating, or browser.language to find slow pages and affected users.
  • For performance alerting, build monitors over the trace data, for example LCP p75 by url.path.
  • For error alerting, build monitors over the log data instead, filtered to records where exception.type is present.

See Also