Skip to content

Instantly share code, notes, and snippets.

@pmeenan
Last active January 18, 2024 23:46
Show Gist options
  • Save pmeenan/5902672 to your computer and use it in GitHub Desktop.
Save pmeenan/5902672 to your computer and use it in GitHub Desktop.
Support routine for adding W3C user timing events to a site. Includes some basic polyfill support for browsers that don't support user timing or navigation timing (though the start time for non-navigation timing support could be improved with IE < 9 to use IE's custom start event).
// Support routines for automatically reporting user timing for common analytics platforms
// Currently supports Google Analytics, Boomerang and SOASTA mPulse
// In the case of boomerang, you will need to map the event names you want reported
// to timer names (for mPulse these need to be custom0, custom1, etc) using a global variable:
// rumMapping = {'aft': 'custom0'};
(function() {
var wtt = function(n, t, b) {
t = Math.round(t);
if (t >= 0 && t < 3600000) {
// Google Analytics
if (!b && window['_gaq'])
_gaq.push(['_trackTiming', 'UserTimings', n, t]);
// Boomerang/mPulse
if (b && window['rumMapping'] && window.rumMapping[n])
BOOMR.plugins.RT.setTimer(window.rumMapping[n], t);
}
};
utReportRUM = function(b){
var m = window.performance.getEntriesByType("mark");
var lm={};
for (i = 0; i < m.length; i++) {
g = 'usertiming';
if (lm[g] == undefined || m[i].startTime > lm[g])
lm[g] = m[i].startTime;
p = m[i].name.match(/([^\.]+)\.([^\.]*)/);
if (p && p.length > 2 &&
(lm[p[1]] == undefined ||
m[i].startTime > lm[p[1]]))
lm[p[1]] = m[i].startTime;
}
for (g in lm)
wtt(g, lm[g], b);
};
utOnLoad = function() {utReportRUM(false);};
if (window['addEventListener'])
window.addEventListener('load', utOnLoad, false);
else if (window['attachEvent'])
window.attachEvent('onload', utOnLoad);
// Boomerang/mPulse support
utSent = false;
BOOMR = window.BOOMR || {};
BOOMR.plugins = BOOMR.plugins || {};
BOOMR.plugins.UserTiming = {
init: function(config) {BOOMR.subscribe('page_ready', function(){
if (!utSent) {
utReportRUM(true);
utSent=true;
BOOMR.sendBeacon();
}
});},
is_complete: function() {return utSent;}
};
})();
/*
Strategically place:
markUserTime('some event');
Through your code to get measurements for when various activities complete. It
will also generate timeline events so you can see them in Chrome's dev tools.
A good use case is to add them inline to sections of your site that are
above-the fold (right after the menu, right after the main story, etc) and also
in the onload handler for any critical above-the-fold images.
*/
;(function () {
var w = typeof window != 'undefined' ? window : exports,
marks = [];
w.performance || (w.performance = {});
w.performance.now || (
w.performance.now = performance.now || performance.webkitNow ||
performance.msNow || performance.mozNow);
if (!w.performance.now){
var s = Date.now ? Date.now() : +(new Date());
if (performance.timing && performance.timing)
s = performance.timing.navigationStart
w.performance.now = (function(){
var n = Date.now ? Date.now() : +(new Date());
return n-s;
});
}
w.performance.mark || (
w.performance.mark =
w.performance.webkitMark ? w.performance.webkitMark :
(function (l) {
marks.push({'name':l,'entryType':'mark','startTime':w.performance.now(),'duration':0});
}));
w.performance.getEntriesByType || (
w.performance.getEntriesByType =
w.performance.webkitGetEntriesByType ? w.performance.webkitGetEntriesByType :
(function (t) {
return t == 'mark' ? marks : undefined;
}));
}());
window.markUserTime = function(l) {
var raf = window['requestAnimationFrame'] ||
(function(callback){setTimeout(callback, 0);});
raf(function(){
window.performance.mark(l);
if (window['console'] && console['timeStamp'])
console.timeStamp(l);
});
};
@pmeenan
Copy link
Author

pmeenan commented Aug 30, 2013

Depends on what you want to do. The 2nd one is a polyfill and support routine for user timing. The first one is a bridge that will report user timing to Google Analytics and Boomerang (or SOASTA).

You can see it live on webpagetest.org if you want to see an example.

@pmeenan
Copy link
Author

pmeenan commented Aug 30, 2013

btw, if you haven't seen it yet, this blog post has a bunch more detail: http://blog.patrickmeenan.com/2013/07/measuring-performance-of-user-experience.html

@bluesmoon
Copy link

better to use setImmediate than setTimeout(0)

@bluesmoon
Copy link

The new mPulse has RT and UT built in. Used your script as a basis for mine.

@jamischarles
Copy link

@bluesmoon Any chance that's open source? Or does Soasta maintain a private fork of boomerang?

@jamischarles
Copy link

@bluesmoon Also, which repo is considered stable? Your fork, or lognormal, or another?

@jamischarles
Copy link

@pmeenan Out of curiosity, why is this step necessary?

if (t >= 0 && t < 3600000) {

https://gist.github.com/pmeenan/5902672#file-user-timing-rum-js-L9

Curiously enough, I left this step out, and am getting lots of IE9 responses that appear to be a timestamp, instead of the delta.

@edmund-huber
Copy link

The spec says http://www.w3.org/TR/performance-timeline/#dom-performance-getentriesbytype that getEntriesByType should always return a sequence. It seems like it would be more correct if the polyfill returned [] instead of undefined when the type is not 'mark'. https://gist.github.com/edmund-huber/d3acd4d510d895cf860a#file-user-timing-js-L39

@micmro
Copy link

micmro commented Oct 23, 2014

There is a bug in line 22:

if (performance.timing && performance.timing)

should probably be

if (w.performance.timing && w.performance.timing.navigationStart)

@bluesmoon
Copy link

@jamischarles, the lognormal one is stable. RT plugin is opensource. UT isn't so much a plugin, nothing really to opensource, though I'm thinking of merging it into RT.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment