// 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); | |
}); | |
}; |
This comment has been minimized.
This comment has been minimized.
Include both scripts? |
This comment has been minimized.
This comment has been minimized.
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. |
This comment has been minimized.
This comment has been minimized.
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 |
This comment has been minimized.
This comment has been minimized.
better to use |
This comment has been minimized.
This comment has been minimized.
The new mPulse has RT and UT built in. Used your script as a basis for mine. |
This comment has been minimized.
This comment has been minimized.
@bluesmoon Any chance that's open source? Or does Soasta maintain a private fork of boomerang? |
This comment has been minimized.
This comment has been minimized.
@bluesmoon Also, which repo is considered stable? Your fork, or lognormal, or another? |
This comment has been minimized.
This comment has been minimized.
@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. |
This comment has been minimized.
This comment has been minimized.
The spec says http://www.w3.org/TR/performance-timeline/#dom-performance-getentriesbytype that |
This comment has been minimized.
This comment has been minimized.
There is a bug in line 22:
should probably be
|
This comment has been minimized.
This comment has been minimized.
@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. |
This comment has been minimized.
(coming soon) WebPagetest will report on any user timing events that were recorded
and also expose a high-level metric for when the last user timing event fired.
With any luck we can also get RUM solutions to start reporting user timing events
and we can significantly loosen our dependency on the (very broken) document
onload times.