This guide provides detailed information about Split's Real User Monitoring (RUM) Agent for Web browsers.
Split's Browser RUM Agent collects events about your users' experience when they visit your web application and sends this information to Split services. This allows you to measure and analyze the impact of feature flag changes on performance metrics.
Language Support
Split's Browser RUM Agent is compatible with EcmaScript 5 syntax and therefore supports the majority of today's popular browsers, with the exception of older browsers like IE. We rely on the browser Beacon API support to reliably send information to Split services for processing. For browsers that do not support Beacon API, the RUM Agent defaults to using Fetch or XHR instead.
Additional Web APIs like Promise, History and Performance APIs, highly available in modern browser, are required to collect some specific events, but not for the regular operation of the agent. See the Events section for specific events and their compatibility.
Initialization
Set up Split's RUM Agent in your code with the following two steps:
1. Import the Agent into your project
Split's RUM Agent is delivered as a NPM package and as a script bundle hosted in a CDN. You can import the Agent into your project using either of the two methods, as shown below.
npm install --save @splitsoftware/browser-rum-agent
<script src="//cdn.split.io/rum-agent/browser-rum-agent-0.3.0.min.js"></script>
2. Setup the Agent
You can initialize the Browser RUM Agent in your code as shown below.
import { SplitRumAgent } from '@splitsoftware/browser-rum-agent';
SplitRumAgent
.setup('YOUR_SDK_KEY')
.addIdentities([
{ key: 'key', trafficType: 'a_traffic_type' },
{ key: 'another_key', trafficType: 'another_traffic_type' }
]);
const { SplitRumAgent } = require('@splitsoftware/browser-rum-agent');
SplitRumAgent
.setup('YOUR_SDK_KEY')
.addIdentities([
{ key: 'key', trafficType: 'a_traffic_type' },
{ key: 'another_key', trafficType: 'another_traffic_type' }
]);
Alternatively, you can initialize the Agent in two parts. First, import the Agent early in the code execution order, and then postpone setting the identities until that information is available.
import { SplitRumAgent } from '@splitsoftware/browser-rum-agent');
SplitRumAgent.setup('YOUR_SDK_KEY');
// In a different file or part of your code, where the identities are available:
SplitRumAgent.addIdentity({ key: 'user_id', trafficType: 'user' });
Configuration
The RUM Agent can be configured to change its default behavior. The following options are available:
SplitRumAgent.setup('YOUR_SDK_KEY', {
/**
* Optional prefix to append to the `eventTypeId` of the events sent to Split.
* For example, if you set the prefix to 'my-app', the event type 'error' will be sent as 'my-app.error'.
* @default ''
*/
prefix: 'my-app',
/**
* The agent posts the queued events data in bulks. This parameter controls the posting rate in seconds.
* @default 30
*/
pushRate: 30,
/**
* The maximum number of event items we want to queue. If we queue more values, events will be dropped until they are sent to Split.
* @default 5000
*/
queueSize: 5000,
});
Events
Split's RUM Agent collects a number of browser events by default and can be extended by registering event collectors. Event collectors collect additional events that are relevant to your application. They are not shipped by default with the Agent itself to avoid increasing your bundle size with unnecessary code.
Event collectors are available when using the NPM package, and can be imported and registered as follows:
import { SplitRumAgent, webVitals, routeChanges, tti } from '@splitsoftware/browser-rum-agent');
SplitRumAgent.register(webVitals());
SplitRumAgent.register(tti());
SplitRumAgent.register(routeChanges());
Refer to the table below and the following sections for more information about the default events and the available event collectors.
Default events
Event type ID | Description | Has value? | Has properties? |
---|---|---|---|
error | Any JavaScript unhandled error and promise rejection | No | { message: string, stack: string } |
page.load.time | Time in milliseconds elapsed until the document is loaded and parsed | Yes | No |
time.to.dom.interactive | Time in milliseconds until the document is ready and before the full page load time. If this time is high, it usually implies that the critical rendering path is complex and that the download of resources will start later. | Yes | No |
Web Vitals
Web Vitals is an initiative by Google to provide unified guidance for quality signals that are essential to delivering a great user experience on the web.
The RUM Agent exports an event collector for Web Vitals, which internally uses Google web-vitals NPM package to collect the Web vitals metrics.
You can set it as follows:
import { SplitRumAgent, webVitals } from '@splitsoftware/browser-rum-agent');
SplitRumAgent.register(webVitals());
By default, the Web Vitals collector will collect all metrics supported by the web-vitals package:
- Core Web Vitals:
- Other metrics:
You can also configure the Web Vitals collector to collect only a subset of the metrics:
SplitRumAgent.register(webVitals({
reportOptions: {
onCLS: true, // collects Cumulative Layout Shift
onFID: { reportAllChanges: true }, // collects First Input Delay and overwrites its default options
onLCP: false, // does not collect Largest Contentful Paint
// other metrics are not collected too: onINP, onFCP, onTTFB
}
}));
Refer to the web-vitals Report options for more information about the available options.
The format of collected events is shown below:
type WebVitalsEvent = {
eventTypeId: 'webvitals.cls' | 'webvitals.fcp' | 'webvitals.fid' | 'webvitals.inp' | 'webvitals.lcp' | 'webvitals.ttfb',
value: number, // value in milliseconds
properties: {
rating: 'good' | 'needsImprovement' | 'poor',
delta: number,
navigationType: 'navigate' | 'reload' | 'back_forward' | 'back-forward-cache' | 'prerender' | 'restore'
}
}
Time to Interactive
Time to Interactive (TTI) is a metric that measures the time from when the page starts loading to when its main sub-resources have loaded and it is capable of reliably responding to user input.
The RUM Agent exports an event collector for TTI, which internally uses tti-polyfill NPM package to collect it.
You can set it as follows:
import { SplitRumAgent, tti } from '@splitsoftware/browser-rum-agent');
SplitRumAgent.register(tti());
Unlike webVitals
, the tti
collector does not support any configuration options.
The format of collected event is shown below:
type TTIEvent = {
eventTypeId: 'time.to.interactive',
value: number, // TTI value in milliseconds
}
Route Changes
Route changes are events that are triggered when the user navigates to a new page in a Single-Page Application (SPA).
The RUM Agent exports an event collector for route changes.
You can set the Agent to collect route change events as follows:
import { SplitRumAgent, routeChanges } from '@splitsoftware/browser-rum-agent');
SplitRumAgent.register(routeChanges());
You can also configure the Agent to collect only a subset of the route changes, by providing a filter callback. For example, to ignore hash (fragment) changes:
SplitRumAgent.register(routeChanges({
filter({ fromUrl, toUrl }) {
return fromUrl.split('#')[0] !== toUrl.split('#')[0];
}
}));
The format of a collected event is:
type RouteChangesEvent = {
eventTypeId: 'route.change',
value: number | undefined, // Value of the `duration` property
properties: {
// URL value before the change, without including the origin (protocol, host and port)
fromUrl: string,
// URL value after the change, without including the origin (protocol, host and port)
toUrl: string,
// Type of URL change
// * `pushState` indicates a new entry added to the history stack, when `history.pushState` is called
// * `replaceState` indicates the entry at the current index in the history stack being replaced, when `history.replaceState` is called
// * `popstate` indicates a change to an arbitrary index in the history stack
historyChangeSource: 'pushState' | 'replaceState' | 'popstate',
/* Browsers that support the Performance Timeline API will include the following properties: */
// Estimated duration of the navigation transition in milliseconds. The calculation is based on a time window of long tasks and resources around the history change event.
duration?: number,
// Value of performance.now() when the navigation started
startTime?: number,
// Time spent on the previous route (`fromUrl`) in milliseconds
timeOnRoute?: number,
}
}
Advanced use cases
Custom properties
Each event for the metrics described above automatically includes three properties that can be use to filter certain events when defining Split metrics for experimentation purposes. Learn more about metric definitions and how to define property filters
name | Description | values |
---|---|---|
connectionType | Speed of connection | 2g, 3g, 4g |
url | The url that generated the metric | |
userAgent | The user agent |
Custom properties can be also added to a tracked event by using the setProperties
method.
SplitRumAgent.setProperties({ 'property_name': 'property_value' }); // set a single or multiple properties as a map object of key/value pairs
const properties = SplitRumAgent.getProperties(); // get all properties as a map object of key/value pairs
SplitRumAgent.removeProperties(); // remove properties
Custom events
There are multiple methods to track custom events:
- using the
track
method - using the specialized
trackError
method - registering a custom event collector
These methods are demonstrated below.
Using the track
method:
SplitRumAgent.track('event_type_id'); // track a single event without value
SplitRumAgent.track('event_type_id', 100); // track a single event with value
SplitRumAgent.track('event_type_id', 100, { 'property_name': 'property_value' }) // track a single event with value and properties
Using the trackError
method:
SplitRumAgent.trackError('error_message'); // Shorthand for track('error', undefined, { message: 'error_message', stack: 'unavailable' })
SplitRumAgent.trackError(errorObject); // Shorthand for track('error', undefined, { message: error.message, stack: error.stack })
Registering a custom event collector:
SplitRumAgent.register(({ track }) => {
track({
eventTypeId: 'event_type_id',
value: 100, // optional value
properties: { 'property_name': 'property_value' } // optional properties
})
});
Async/Lazy loading
Including the RUM Agent code in your application bundle will increase its size and impact the captured performance metrics.
If you want to avoid this, you can load the Agent asynchronously by lazy loading, for example by using dynamic imports:
import('@splitsoftware/rum-agent').then(({ SplitRumAgent, webVitals }) => {
SplitRumAgent
.setup('YOUR_SDK_KEY')
.register(webVitals());
}).catch(err => {
console.error('Error loading Agent', err);
});
Missing error events on lazy loading
When using lazy loading, the RUM agent will normally not be able to capture any errors that occur before the agent has finished loading. To solve this, you can place the following script tag in the <head>
section of your page.
<script>
(function(w){
var g=w.__error={e1:[],l1(e){g.e1.push(e)},e2:[],l2(e){g.e2.push(e)}}
w.addEventListener('error', g.l1)
w.addEventListener('unhandledrejection', g.l2)
}(window))
</script>
This script captures regular JavaScript errors and unhandled promise rejections, and stores them in memory. Once the RUM agent loads, it sends the captured errors to Split services for processing, ensuring that even errors occurring before the agent is fully loaded are not missed.
Comments
0 comments
Please sign in to leave a comment.