Skip to content

Commit 249c065

Browse files
authored
feat: RUM v2 (#344)
1 parent bdf4fce commit 249c065

File tree

1 file changed

+107
-80
lines changed

1 file changed

+107
-80
lines changed

scripts/scripts.js

Lines changed: 107 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -19,87 +19,118 @@
1919
* @param {string} data.target subject of the checkpoint event,
2020
* for instance the href of a link, or a search term
2121
*/
22-
export function sampleRUM(checkpoint, data = {}) {
23-
const SESSION_STORAGE_KEY = 'aem-rum';
24-
sampleRUM.baseURL = sampleRUM.baseURL || new URL(window.RUM_BASE == null ? 'https://rum.hlx.page' : window.RUM_BASE, window.location);
25-
sampleRUM.defer = sampleRUM.defer || [];
26-
const defer = (fnname) => {
27-
sampleRUM[fnname] = sampleRUM[fnname]
28-
|| ((...args) => sampleRUM.defer.push({ fnname, args }));
29-
};
30-
sampleRUM.drain = sampleRUM.drain
31-
|| ((dfnname, fn) => {
32-
sampleRUM[dfnname] = fn;
33-
sampleRUM.defer
34-
.filter(({ fnname }) => dfnname === fnname)
35-
.forEach(({ fnname, args }) => sampleRUM[fnname](...args));
36-
});
37-
sampleRUM.always = sampleRUM.always || [];
38-
sampleRUM.always.on = (chkpnt, fn) => {
39-
sampleRUM.always[chkpnt] = fn;
40-
};
41-
sampleRUM.on = (chkpnt, fn) => {
42-
sampleRUM.cases[chkpnt] = fn;
43-
};
44-
defer('observe');
45-
defer('cwv');
22+
export function sampleRUM(checkpoint, data) {
23+
// eslint-disable-next-line max-len
24+
const timeShift = () => (window.performance ? window.performance.now() : Date.now() - window.hlx.rum.firstReadTime);
4625
try {
4726
window.hlx = window.hlx || {};
27+
sampleRUM.enhance = () => {};
4828
if (!window.hlx.rum) {
49-
const usp = new URLSearchParams(window.location.search);
50-
const weight = (usp.get('rum') === 'on') ? 1 : 100; // with parameter, weight is 1. Defaults to 100.
51-
const id = Array.from({ length: 75 }, (_, i) => String.fromCharCode(48 + i)).filter((a) => /\d|[A-Z]/i.test(a)).filter(() => Math.random() * 75 > 70).join('');
52-
const random = Math.random();
53-
const isSelected = (random * weight < 1);
54-
const firstReadTime = window.performance ? window.performance.timeOrigin : Date.now();
55-
const urlSanitizers = {
56-
full: () => window.location.href,
57-
origin: () => window.location.origin,
58-
path: () => window.location.href.replace(/\?.*$/, ''),
59-
};
60-
// eslint-disable-next-line max-len
61-
const rumSessionStorage = sessionStorage.getItem(SESSION_STORAGE_KEY) ? JSON.parse(sessionStorage.getItem(SESSION_STORAGE_KEY)) : {};
62-
// eslint-disable-next-line max-len
63-
rumSessionStorage.pages = (rumSessionStorage.pages ? rumSessionStorage.pages : 0) + 1 + /* noise */ (Math.floor(Math.random() * 20) - 10);
64-
sessionStorage.setItem(SESSION_STORAGE_KEY, JSON.stringify(rumSessionStorage));
29+
const param = new URLSearchParams(window.location.search).get('rum');
30+
const weight = (window.SAMPLE_PAGEVIEWS_AT_RATE === 'high' && 10)
31+
|| (window.SAMPLE_PAGEVIEWS_AT_RATE === 'low' && 1000)
32+
|| (param === 'on' && 1)
33+
|| 100;
34+
const id = Math.random().toString(36).slice(-4);
35+
const isSelected = param !== 'off' && Math.random() * weight < 1;
6536
// eslint-disable-next-line object-curly-newline, max-len
66-
window.hlx.rum = { weight, id, random, isSelected, firstReadTime, sampleRUM, sanitizeURL: urlSanitizers[window.hlx.RUM_MASK_URL || 'path'], rumSessionStorage };
67-
}
68-
69-
const { weight, id, firstReadTime } = window.hlx.rum;
70-
if (window.hlx && window.hlx.rum && window.hlx.rum.isSelected) {
71-
const knownProperties = ['weight', 'id', 'referer', 'checkpoint', 't', 'source', 'target', 'cwv', 'CLS', 'FID', 'LCP', 'INP', 'TTFB'];
72-
const sendPing = (pdata = data) => {
73-
// eslint-disable-next-line max-len
74-
const t = Math.round(window.performance ? window.performance.now() : (Date.now() - firstReadTime));
75-
// eslint-disable-next-line object-curly-newline, max-len, no-use-before-define
76-
const body = JSON.stringify({ weight, id, referer: window.hlx.rum.sanitizeURL(), checkpoint, t, ...data }, knownProperties);
77-
const url = new URL(`.rum/${weight}`, sampleRUM.baseURL).href;
78-
navigator.sendBeacon(url, body);
79-
// eslint-disable-next-line no-console
80-
console.debug(`ping:${checkpoint}`, pdata);
37+
window.hlx.rum = {
38+
weight,
39+
id,
40+
isSelected,
41+
firstReadTime: window.performance ? window.performance.timeOrigin : Date.now(),
42+
sampleRUM,
43+
queue: [],
44+
collector: (...args) => window.hlx.rum.queue.push(args),
8145
};
82-
sampleRUM.cases = sampleRUM.cases || {
83-
load: () => sampleRUM('pagesviewed', { source: window.hlx.rum.rumSessionStorage.pages }) || true,
84-
cwv: () => sampleRUM.cwv(data) || true,
85-
lazy: () => {
86-
// use classic script to avoid CORS issues
46+
if (isSelected) {
47+
const dataFromErrorObj = (error) => {
48+
const errData = { source: 'undefined error' };
49+
try {
50+
errData.target = error.toString();
51+
errData.source = error.stack
52+
.split('\n')
53+
.filter((line) => line.match(/https?:\/\//))
54+
.shift()
55+
.replace(/at ([^ ]+) \((.+)\)/, '$1@$2')
56+
.replace(/ at /, '@')
57+
.trim();
58+
} catch (err) {
59+
/* error structure was not as expected */
60+
}
61+
return errData;
62+
};
63+
64+
window.addEventListener('error', ({ error }) => {
65+
const errData = dataFromErrorObj(error);
66+
sampleRUM('error', errData);
67+
});
68+
69+
window.addEventListener('unhandledrejection', ({ reason }) => {
70+
let errData = {
71+
source: 'Unhandled Rejection',
72+
target: reason || 'Unknown',
73+
};
74+
if (reason instanceof Error) {
75+
errData = dataFromErrorObj(reason);
76+
}
77+
sampleRUM('error', errData);
78+
});
79+
80+
sampleRUM.baseURL = sampleRUM.baseURL || new URL(window.RUM_BASE || '/', new URL('https://rum.hlx.page'));
81+
sampleRUM.collectBaseURL = sampleRUM.collectBaseURL || sampleRUM.baseURL;
82+
sampleRUM.sendPing = (ck, time, pingData = {}) => {
83+
// eslint-disable-next-line max-len, object-curly-newline
84+
const rumData = JSON.stringify({
85+
weight,
86+
id,
87+
referer: window.location.href,
88+
checkpoint: ck,
89+
t: time,
90+
...pingData,
91+
});
92+
const urlParams = window.RUM_PARAMS
93+
? `?${new URLSearchParams(window.RUM_PARAMS).toString()}`
94+
: '';
95+
const { href: url, origin } = new URL(
96+
`.rum/${weight}${urlParams}`,
97+
sampleRUM.collectBaseURL,
98+
);
99+
const body = origin === window.location.origin
100+
? new Blob([rumData], { type: 'application/json' })
101+
: rumData;
102+
navigator.sendBeacon(url, body);
103+
// eslint-disable-next-line no-console
104+
console.debug(`ping:${ck}`, pingData);
105+
};
106+
sampleRUM.sendPing('top', timeShift());
107+
108+
sampleRUM.enhance = () => {
109+
// only enhance once
110+
if (document.querySelector('script[src*="rum-enhancer"]')) return;
111+
const { enhancerVersion, enhancerHash } = sampleRUM.enhancerContext || {};
87112
const script = document.createElement('script');
88-
script.src = new URL('.rum/@adobe/helix-rum-enhancer@^1/src/index.js', sampleRUM.baseURL).href;
113+
if (enhancerHash) {
114+
script.integrity = enhancerHash;
115+
script.setAttribute('crossorigin', 'anonymous');
116+
}
117+
script.src = new URL(
118+
`.rum/@adobe/helix-rum-enhancer@${enhancerVersion || '^2'}/src/index.js`,
119+
sampleRUM.baseURL,
120+
).href;
89121
document.head.appendChild(script);
90-
return true;
91-
},
92-
};
93-
sendPing(data);
94-
if (sampleRUM.cases[checkpoint]) {
95-
sampleRUM.cases[checkpoint]();
122+
};
123+
if (!window.hlx.RUM_MANUAL_ENHANCE) {
124+
sampleRUM.enhance();
125+
}
96126
}
97127
}
98-
if (sampleRUM.always[checkpoint]) {
99-
sampleRUM.always[checkpoint](data);
128+
if (window.hlx.rum && window.hlx.rum.isSelected && checkpoint) {
129+
window.hlx.rum.collector(checkpoint, data, timeShift());
100130
}
131+
document.dispatchEvent(new CustomEvent('rum', { detail: { checkpoint, data } }));
101132
} catch (error) {
102-
// something went wrong
133+
// something went awry
103134
}
104135
}
105136

@@ -227,6 +258,9 @@ function updateSectionsStatus($main) {
227258
break;
228259
} else {
229260
section.setAttribute('data-section-status', 'loaded');
261+
if (i === 0 && sampleRUM.enhance) {
262+
sampleRUM.enhance();
263+
}
230264
}
231265
}
232266
}
@@ -517,6 +551,8 @@ async function loadPage(doc) {
517551
function initHlx() {
518552
window.hlx = window.hlx || {};
519553
window.hlx.lighthouse = new URLSearchParams(window.location.search).get('lighthouse') === 'on';
554+
window.hlx.RUM_MASK_URL = 'full';
555+
window.hlx.RUM_MANUAL_ENHANCE = true;
520556
window.hlx.codeBasePath = '';
521557

522558
const scriptEl = document.querySelector('script[src$="/scripts/scripts.js"]');
@@ -567,9 +603,7 @@ const LCP_BLOCKS = ['hero']; // add your LCP blocks to the list
567603
const PRODUCTION_DOMAINS = ['adobe.design'];
568604
const ICON_ROOT = '/icons';
569605

570-
sampleRUM('top');
571-
window.addEventListener('load', () => sampleRUM('load'));
572-
document.addEventListener('click', () => sampleRUM('click'));
606+
sampleRUM();
573607

574608
loadPage(document);
575609

@@ -713,10 +747,6 @@ async function loadLazy(doc) {
713747

714748
loadCSS(`${window.hlx.codeBasePath}/styles/lazy-styles.css`);
715749
addFavIcon(`${window.hlx.codeBasePath}/icon.svg`);
716-
717-
sampleRUM('lazy');
718-
sampleRUM.observe(main.querySelectorAll('div[data-block-name]'));
719-
sampleRUM.observe(main.querySelectorAll('picture > img'));
720750
}
721751

722752
/**
@@ -728,9 +758,6 @@ function loadDelayed() {
728758
setTimeout(() => {
729759
loadScript('https://assets.adobedtm.com/a7d65461e54e/9ee19a80de10/launch-882c01867cbb.min.js');
730760
}, 4000);
731-
732-
// Core Web Vitals RUM collection
733-
sampleRUM('cwv');
734761
}
735762

736763
/*

0 commit comments

Comments
 (0)