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 UMD 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="https://cdn.split.io/rum-agent/browser-rum-agent-0.7.1.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' }
]);
<!-- Add the following script tags as high as possible in the `<head>` section of the main page -->
<script src="https://cdn.split.io/rum-agent/browser-rum-agent-0.7.1.min.js"></script>
<script>
window.SplitRumAgent
.setup('YOUR_SDK_KEY')
.addIdentities([
{ key: 'key', trafficType: 'a_traffic_type' },
{ key: 'another_key', trafficType: 'another_traffic_type' }
]);
</script>
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' });
<script src="https://cdn.split.io/rum-agent/browser-rum-agent-0.7.1.min.js"></script>
<script>
window.SplitRumAgent.setup('YOUR_SDK_KEY');
</script>
<!-- Later in the page, anywhere where you can add javascript, add: -->
<script>
window.SplitRumAgent.addIdentity({ key: 'user_id', trafficType: 'user' });
</script>
Identity objects consist of a key and a traffic type. The traffic type value must match the name of a traffic type that you have defined in the Split Management Console.
These identities are used to associate the events captured by the RUM Agent to some user, before sending them to Split services. If you provide more than one identity, the captured events will be duplicated and sent to Split services for each identity.
Configuration
The RUM Agent can be configured to change its default behavior. The following options are available:
- Prefix: 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'
. It defaults to'split.rum'
. - Push Rate: The Agent posts the queued events data in bulks. This parameter controls the posting rate in seconds. The default value is
30
. - Queue Size: 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. The default value is
5000
. - User Consent: User consent status used to control the tracking of events and impressions. Possible values are
'GRANTED'
,'DECLINED'
, and'UNKNOWN'
. The default value is'GRANTED'
. See the User consent section for details.
These options can be configured programmatically, as demonstrated below:
SplitRumAgent.setup('YOUR_SDK_KEY', {
prefix: 'my-app',
pushRate: 30,
queueSize: 5000,
userConsent: 'GRANTED'
});
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, or with a "full" version of the UMD bundle hosted in our CDN. They can be imported and registered as follows:
import { SplitRumAgent, webVitals, routeChanges } from '@splitsoftware/browser-rum-agent';
SplitRumAgent.register(webVitals());
SplitRumAgent.register(routeChanges());
<script src="https://cdn.split.io/rum-agent/browser-rum-agent-0.7.1.full.min.js"></script>
<script>
SplitRumAgent.register(SplitRumAgent.webVitals());
SplitRumAgent.register(SplitRumAgent.routeChanges());
</script>
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 fully loaded and parsed. It is equivalent to the time until the load event is fired. | 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. Related to the domInteractive property. | 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 the 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: {
// collects only the core web-vitals
onCLS: true,
onFID: true,
onLCP: true,
// other web-vital metrics are not collected
}
}));
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',
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 the 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 shown below:
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,
}
}
Automatic metric creation
Split will automatically create metrics for a subset of the event types received from the Browser RUM Agent. These "out of the box metrics" are auto-created for you:
Event type | Metric name |
---|---|
split.rum.error | Count of Application Errors - Split Agents |
split.rum.page.load.time | Average Page Load Time - Split Agents |
split.rum.webvitals.fcp | Average FCP (Web Vitals: Time to First Contentful Paint) - Split Agents |
split.rum.webvitals.fid | Average FID (Web Vitals: Time to First Input Delay) - Split Agents |
split.rum.webvitals.cls | Average CLS (Web Vitals: Cumulative Layout Shift) - Split Agents |
split.rum.webvitals.inp | Average INP (Web Vitals: Interaction to Next Paint) - Split Agents |
split.rum.webvitals.lcp | Average LCP (Web Vitals: Largest Contentful Paint) - Split Agents |
split.rum.webvitals.ttfb | Average TTFB (Web Vitals: Time to First Byte) - Split Agents |
For a metric that was auto-created, you can manage the definition and alert policies like you would for any other metric. If you delete a metric that was auto-created, Split will not re-create the metric, even if the event type is still flowing.
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 it, for example by using dynamic imports or async script tags:
import('@splitsoftware/browser-rum-agent').then(({ SplitRumAgent, webVitals }) => {
SplitRumAgent
.setup('YOUR_SDK_KEY')
.register(webVitals())
.addIdentity({ key: 'user_id', trafficType: 'user' });
}).catch(err => {
console.error('Error loading Agent', err);
});
<script async src="https://cdn.split.io/rum-agent/browser-rum-agent-0.7.1.min.js" onload="setupRumAgent()"></script>
<script>
function setupRumAgent() {
SplitRumAgent
.setup('YOUR_SDK_KEY')
.addIdentity({ key: 'user_id', trafficType: 'user' });
}
</script>
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:function(e){g.e1.push(e);},e2:[],l2:function(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.
User consent
By default the Agent will send events to Split cloud, but you can disable this behavior until user consent is explicitly granted.
The userConsent
configuration parameter lets you set the initial consent status of the Agent, and the SplitRumAgent.setUserConsent(boolean)
method lets you grant (enable) or decline (disable) dynamic event tracking.
There are three possible initial states:
-
'GRANTED'
: The user grants consent for tracking events. The Agent sends them to Split cloud. This is the default value ifuserConsent
param is not defined. -
'DECLINED'
: The user declines consent for tracking events. The Agent does not send them to Split cloud. -
'UNKNOWN'
: The user neither grants nor declines consent for tracking events. The Agent tracks them in its internal storage, and eventually either sends them or not if the consent status is updated to'GRANTED'
or'DECLINED'
respectively.
The status can be updated at any time with the setUserConsent
method.
Working with user consent is demonstrated below.
SplitRumAgent.setup('YOUR_SDK_KEY', {
// Overwrites the initial consent status of the Agent, which is 'GRANTED' by default.
// 'UNKNOWN' status represents that the user has neither granted nor declined consent for tracking data,
// so the Agent will locally track data but not send it to Split cloud until consent is changed to 'GRANTED'.
userConsent: 'UNKNOWN'
});
// `getUserConsent` method returns the current consent status.
SplitRumAgent.getUserConsent() === 'UNKNOWN';
// `setUserConsent` method lets you update the consent status at any time.
// Pass `true` for 'GRANTED' and `false` for 'DECLINED'.
SplitRumAgent.setUserConsent(true); // Consent status changed from 'UNKNOWN' to 'GRANTED'. Data will be sent to Split cloud.
SplitRumAgent.getUserConsent() === 'GRANTED';
SplitRumAgent.setUserConsent(false); // Consent status changed from 'GRANTED' to 'DECLINED'. Data will not be sent to Split cloud.
SplitRumAgent.getUserConsent() === 'DECLINED';
Example apps
The following repository contains different example apps that demonstrate how to use Split's Browser RUM Agent:
Comments
0 comments
Please sign in to leave a comment.