-
Notifications
You must be signed in to change notification settings - Fork 11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve RUM observability and monitoring of single page apps #27
Comments
@paulirish @nicjansma @bluesmoon fyi, would love to hear your thoughts.. It'd be also really great to pull in some framework developers to see if there is interest and/or previous or existing work that we should consider. |
(1) Some sort of PerformanceEntry for In fact, a PerformanceEntry helps in another way -- when third-party monitoring solutions load "late" (e.g. after the thing they want to monitor), they miss the events if they're not logged elsewhere. Having a PerformanceEntry means we can review history to check on things that already happened. (2) For boomerang.js, we listen to SPA framework events, pushState/replaceState and XHRs to guess when an "important" user interaction begins so we can do additional monitoring for soft navigations. I like the idea of a standard/convention for mark names that frameworks could opt-in to to help us with this. Or, the monitoring solutions themselves could fire them if the framework doesn't support it. (Though listening for UserTiming events will really require global PerformanceObserver support so we're not polling |
I wonder if we need some kind of "ResourceGroup" definition to separate out
resources requested as part of parallel SPA interactions. For example, if
two separate sections of the page need to be updated and the update happens
in parallel, it's very hard right now for us to determine which resources
belong to which subsection.
It would be good if we could give framework authors the ability to mark
resources as belonging to a certain group when they are requested. Not
sure how this would be done with resources requested via injecting
innerHTML into the page though.
|
I'm a huge fan of Entries added for pushState() and popstate event. The latter is observable, so might not be neccessary, but the former is key information and otherwise unobservable. Chrome DevTools is so ready to add support for #2. Once we have a modicum of consensus on naming, we can ship an experiment to try things out. cc @mjackson for insight as framework author of mjackson/history and ReactTraining/react-router. How does the two proposals up top sound to you? |
@nicjansma is the code public / do you have any pointers?
That's not true as of today. Assuming such events are emitted via PerformanceObserver, you have to register the observer to get the notifications. We've had a few discussions in the past about ~buffering such events before onload, but we don't have a resolution on that.
You can avoid polling if you feature detect PerformanceObserver -- correct?
That's an interesting idea. However, can't think of any obvious way to enable this on a per resource level. The crude version of this would be to divide the timeline into separate epoch's.. but that doesn't address overlapping cases and arguably achievable by a mark convention.
@paulirish I'm not too familiar with all the various events, but what about hashchange? Stepping back.. I'd love to see someone put together a library that captures all these events -- I think it would help clarify what we can and cannot capture, and whether perf timeline integration is necessary.
Cool! Re-reading through the catapult docs, the convention they settled on is pretty simple:
I'm not sure how I feel about the base64 args yet (and if we need that), but the group:task pattern seems straightforward and useful. With that in mind, focusing the latter.. If we use that convention to group metrics, do we need to also establish additional conventions for the names of the group? E.g. |
Thanks for the ping, @paulirish. I'll do my best to be useful :)
I agree with you here, @igrigorik.
That sounds like a reasonable approach. Can you give me some examples of names that you might expect to see? That would help me better understand what you're suggesting. Maybe something like these?
There are a host of other things that people traditionally need to do when building stuff on the history API. It usually boils down to one of:
It may be interesting to consider additional marks for these categories of tasks. |
@igrigorik I could be misinterpreting what @nicjansma meant in his comment, but I think this issue is relevant to third-party analytics solutions that want to help SPAs (or any site, really) track User Timing metrics. I'm specifically referring to the issue brought up in w3c/user-timing#3 that relates to adding additional metadata to performance entries. Let me offer an example to help make the point. Suppose I'm writing a third-party analytics library that allows developers to register start and end mark names and automatically measure and report the duration between them to an analytics back end. In this example I asynchronously load my analytics library and once it's loaded I tell it to measure and report the duration between The problem is if this library takes a while to load, it's possible the user will have made multiple page fetches and now my analytics library can't know which fetch duration is associated with which page request (and thus which page path). In the discussion of w3c/user-timing#3 you suggest that it'd be easy for libraries to wrap the I think for any sort of generic, platform-based marks on |
Boomerang measures React and other SPAs today via this plugin that hooks into
You're right, I forgot that UserTiming and ResouceTiming are special cases in that they have buffers that live until cleared or full. I agree that our discussion on how long to keep PerformanceObserver entries -- until at least onload? -- would be useful to continue. While it's not guaranteed RUM solutions will load by onload (especially if they're using async loader patterns), we could assume that if the site is interested enough they could make sure that either the RUM script or the loader script could turn on the right observers by onload.
Absolutely, yes -- just pointing out we're looking forward to global PerformanceObserver support :)
Great point! |
Yep, this all makes sense. That said, speccing and implementing support for such mechanism is at least multiple quarters out. In the meantime, is there a simple(r) convention-based solution that we could agree on and prototype? E.g.. while base64-args approach (see #27 (comment)) is not pretty, it can accommodate this. @mjackson I was hoping you could help define some of the groups/metrics :-)
Is "navigation" right group name? Are there other group names we care about? |
How about these?
I also like the group name You already most likely have groups for gathering metrics around loading resources, but it may be interesting to somehow correlate them with specific navigation events. |
It's not terribly hard to polyfill a possible future API. Here's an example that extends the (function() {
if (window.PerformanceMark && 'data' in window.PerformanceMark.prototype) return;
function noop() {};
function getEntry(name) {return window.performance.getEntriesByName(name).slice(-1)[0];}
window.performance = window.performance || {};
window.performance.mark = window.performance.mark || noop;
window.performance.measure = window.performance.measure || noop;
window.performance.getEntriesByName = window.performance.getEntriesByName || noop;
var nativePerformanceMark = window.performance.mark;
window.performance.mark = function(name, data) {
nativePerformanceMark.call(window.performance, name);
getEntry(name).data = data;
};
var nativePerformanceMeasure = window.performance.measure;
window.performance.measure = function(name, startName, endName, data) {
nativePerformanceMeasure.call(window.performance, name, startName, endName);
var measure = getEntry(name);
measure.startEntry = getEntry(startName);
measure.endEntry = getEntry(endName);
measure.data = data;
};
}()); And this minifies to an amount I'd be comfortable inlining in the !function(n){function r(){}function a(r){return n[t][i](r).slice(-1)[0]}var t="performance",e="mark",c="measure",i="getEntriesByName",o="PerformanceMark",f="data";if(!(n[o]&&f in n[o].prototype)){n[t]=n[t]||{},n[t][e]=n[t][e]||r,n[t][c]=n[t][c]||r,n[t][i]=n[t][i]||r;var u=n[t][e];n[t][e]=function(r,e){u.call(n[t],r),a(r)[f]=e};var l=n[t][c];n[t][c]=function(r,e,c,i){l.call(n[t],r,e,c);var o=a(r);o.startEntry=a(e),o.endEntry=a(c),o[f]=i}}}(window); I've recently been thinking about writing an autotrack plugin to automatically send relevant performance entries to GA, and I think these additions would make that doable. Without this it doesn't really work because users can't specify relevant dimensions when making marks. |
@philipwalton neat. One gotcha I can see with the above is that perf timeline does not guarantee that the entry queued into the global buffer is the same entry that's exposed to all the PerfObservers.. That said, in practice the above does work today in Chrome and FF nightly -- observers receive entry with Stepping back, roughly the requirements we have so far are:
UT already provides 1, so we're solving for 2 and 3.
^ pushing these into a dictionary might be a better solution, as it's explicit about what you're assigning and you're not forced to rely on parameter order / pass in null values for stuff you don't care about. But then this also leads me to #28... if we solve that, perhaps this entire discussion is moot.
Yep, I agree. |
With User Timing L2 going to CR, I think we're now in position to start work on UT L3 and Custom entries that can help address this problem space. Let's continue this discussion on w3c/user-timing#40. Closing. |
(discussed at TPAC 2016; rough notes and summary of discussed ideas below)
SPAs make automated monitoring tricky: with page navigations/reloads automated solutions have clear markers for start/finish of major user interactions, whereas within a single-page-app it's often very hard to automatically identify when such transitions occur. The emerging pattern is: listen for any pushState changes, custom instrument popular frameworks to hook into major lifecycle events.
Question: is there anything we (webperf group) could or should do to assist with the above?
In no particular order, some ideas and points that were discussed:
{well-known-prefix}-{tbd-format}
convention for SPA's that can allow apps and framework authors to decorate their applications with meaningful transitions? In turn, devtools could use such marks to decorate local timeline to assist with local development.. and RUM solutions can harvest same data in production. Chrome's traceviewer did this last year, we should investigate what they did.The text was updated successfully, but these errors were encountered: