govuk_publishing_components: Real User Metrics
Real user metrics (RUM) allow a user's browser to report on how a page loaded using the Performance API. The tool that we use to do this is called LUX - short for Live User Experience - and is run by SpeedCurve.
The benefit of RUM is that it shows how pages are performing in real situations, rather than on synthetic tests. This means that we don't have to guess at what conditions might be - RUM provides insight into what conditions actually are.
The LUX scripts should only be loaded when a user has opted into usage tracking. This is done using the
rum-loader script, which checks if the
usage cookie is
true and then loads the required scripts. This is already part of the Public Layout component.
How it works
The scripts for the real user metrics are loaded from our servers - this allows us to know what is contained within the scripts and reduce the risk of unwelcome things being added to GOV.UK. Normally LUX is loaded from SpeedCurve's servers, so our setup is unusual.
There are three scripts involved.
the loader is a script that we wrote. If cookies have been accepted, it finds the LUX script, attaches it to the DOM and executes it. If cookies haven't been accepted, it sets a listener for the
- the measurer measures performance from the start of page load, but doesn't report anything. This file shouldn't ever change. The source is from the RUM dashboard, in Settings, Edit RUM, at the end. We don't use this in the way SpeedCurve recommends, because we load the scripts ourselves.
- the reporter is the script that the loader pulls down and attaches to the DOM. This file is compiled as a separate asset and isn't included until users consent to cookies.
The reporter is the file that SpeedCurve occasionally changes (in SpeedCurve's repo it's called
lux.js). Because we load our own modified copy of this script we currently have to manually download and update it. The script is audited before being updated and then two extra lines are added to make sure it works correctly. These two lines set the customer ID and set the sampling rate.
The customer ID is an identifier for the site using LUX, not for the user visiting the site. It won't change from page to page, or from visitor to visitor.
lux.js from SpeedCurve's CDN, the customer ID is appended to the end of the URI as a query string. The script looks for a script in the DOM with a source of
lux.js, and from that extracts the customer ID.
Rails adds a fingerprint to the URI which means that
lux.js becomes (for example)
lux.self-7137780d5344a93190a2c698cd660619d4197420b9b1ef963b639a825a6aa5ff.js and the script can't find itself. Because of this that part of the script would fail. Instead, we modify the
getCustomerId() function to simply return
Because of this the customer ID needs to be set in the
LUX.customerid = 47044334
LUX defaults to sending every event to SpeedCurve - this can be changed by setting
LUX.samplerate to a integer:
LUX.samplerate = 1 // Whole number from 1 to 100.
This then only sends 1% of visits.
This must be set at the top of the main
LUX function or it will default to 100% sample rate.
Debugging (bonus mode)
LUX.debug = true
Debug is turned off by default - setting this to
true it will log what LUX is doing in the browser's console.
LUX.getDebug() in the browser's console will show the history of what's happened whether
Sampling can also be forced by placing
LUX.forceSample() at the end of the file:
How to update to a new version
2nd line have an icinga alert set up to tell us when a new version of LUX is available. This checks the live LUX script for a variable called
SCRIPT_VERSION. Since the file is minified it is actually looking for the pattern
version_number is currently
302. If it does not match this, an alert is triggered.
The icinga alert was originally added in this PR, for reference.
Getting the new code
You will need to download the new script from the SpeedCurve github repo. This is a private repo and you will need to request access. Matt Hobbs (head of frontend) is our point of contact for this. Once you have access it's worth turning on notifications for this repo in case the icinga alert fails.
The script is written in TypeScript so you will first need to
npm install and
lux-reporter.js by copying
dist/lux.js from the SpeedCurve github repo. This is a case of copying and pasting, reformatting, and updating to include the variables mentioned earlier. Try to format the file so that indenting differences don't make reviewing difficult.
Instructions for how to update are in our copy of the file. In summary:
samplerateare the things we need to set (copy them from the current version)
- the structure of the file sometimes changes so you might need to comb through to find them (
samplerateis currently around line 443, but might change)
customeridhas to be at the end of the file
You can test the changes locally to ensure the file has been updated correctly.
- temporarily set the
debugoption in the reporter to
truefor testing purposes
- load your local system and accept cookies
- paste this command into the browser console:
undefinedbut the output will be in your copy/paste buffer)
- visit the SpeedCurve debugging tool and paste the output into it, and check the output for errors
If you're using docker to run the application, your local server will have a
dev.gov.uk domain name and it is possible to set up SpeedCurve to see the data from it. You will need to uncomment the
forceSample line to make this work.
We've added an extra bit to the end of the measurer script to determine what protocol is in use - HTTP 1, 2 or 3. This shouldn't need any updating as the measurer shouldn't change.