Measure navigation timing with javascript

Historically, site administrators would measure their site performance by timing individual page renders. If your site is serving pages in less than 500ms, it must be pretty fast, right? The full picture is significantly more complicated.

With the advent of decent browser-based timing information, developers and admins realized just how much of the picture they were previously missing. Now, with sites like yotta, you can get this information without even sullying your own browser.

Notice that the "signup" HTML itself came back in just 158 milliseconds. This is the number you would see in typical apache log analyzer tools. But according to the browser based timing, the user did not see anything on the page for over 900ms. Even then, they could not fully interact with the page for over 2 seconds.

Instead of getting this information from expensive third party monitoring services, what if you could harvest it from your actual visitors? Enter the W3C Navigation Timing spec. It turns out browser vendors are way ahead of us.

User latency is an important quality benchmark for Web Applications. While JavaScript-based mechanisms can provide comprehensive instrumentation for user latency measurements within an application, in many cases, they are unable to provide a complete end-to-end latency picture.

In turns out that you can access this information right now, at least in Chrome latest and IE 9.0. It's exposed as a simple javascript object.

So how do you log this information on the server side? There are many ways, but recently we solved this problem on one of our sites by sticking the values into a cookie. Then you can examine this cookie server side on a subsequent page request. From there, we decided to log to a file and create pretty graphs with Splunk.

function store_client_performance_data() {

    var t = performance.timing;

    // Before the load event fires, the loadEventEnd value will be zero
    if (t.loadEventEnd > 0) {

        var earliestTime = t.navigationStart;

        // Redirects from a different origin cause navigationStart to
        // be zeroed out.  See NavigationTiming spec:
        //
        // https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html
        //
        if (earliestTime == 0) {
            earliestTime = t.fetchStart;
        }

        fetchStartTime = t.fetchStart - earliestTime;
        responseEndTime = t.responseEnd - earliestTime;
        loadEventEndTime = t.loadEventEnd - earliestTime;

        jQuery.cookie("clientSidePerformance",
                      fetchStartTime + "|" +
                      responseEndTime + "|" +
                      loadEventEndTime,
                      {"path": "/" });

    } else {
        setTimeout("store_client_performance_data()", 1000);
    }
}

if (Object.prototype.hasOwnProperty.call(window, "performance")) {
    setTimeout("store_client_performance_data()", 1000);
}

The only tricky part here is that you can't be sure when the page will stop loading. Instead, we run this function every second until loadEventEnd is non-zero.



I'm currently working at NerdWallet, a startup in San Francisco trying to bring clarity to all of life's financial decisions. We're hiring like crazy. Hit me up on Twitter, I would love to talk.

Follow @chase_seibert on Twitter