Add Instrumentation

Configuration

The 'Add Instrumentation' filter is enabled by specifying:

Apache:
ModPagespeedEnableFilters add_instrumentation
Nginx:
pagespeed EnableFilters add_instrumentation;

in the configuration file.

Description

The 'Add Instrumentation' filter injects two small blocks of JavaScript into every HTML page. These blocks measure the time the client spends loading and rendering the page, and report that measurement back to the server.

Operation

An empty HTML file passed through the add_instrumentation filter will come out like this (the inserted whitespace is for documentation readability):

<html>
  <head>
    <script type='text/javascript'>
      window.mod_pagespeed_start = Number(new Date());
  </script>
  </head>
  <body>
    <script type='text/javascript'>
      function g(){
        new Image().src = '/mod_pagespeed_beacon?ets=load:'
          + (Number(new Date())-window.mod_pagespeed_start);
      };
      var f = window.addEventListener;
      if (f) {
        f('load',g,false);
      } else {
        f=window.attachEvent;
        if (f) {
          f('onload',g);
        }
      }
    </script>
  </body>
</html>

Example

You can see the filter in action at www.modpagespeed.com on this example.

Methodology

The first script tag is inserted at the beginning of the document's <head>. It simply records the current time in a global variable. This is placed as early in the document as possible in order to best approximate the beginning of the page load.

The second script tag is inserted at the end of the document's <body>, so as to minimally interfere with page rendering. It registers a listener for the page's onLoad event; this listener computes the total time since the first script tag ran, and sends the result back to the server asynchronously by using a fake Image object. This technique should work on most modern browsers. [1].

The add_instrumentation filter sends a beacon after the page onload handler is called. The user might navigate to a new URL before this. If you enable the following directive, the beacon is sent as part of an onbeforeunload handler, for pages where navigation happens before the onload event.

Apache:
ModPagespeedReportUnloadTime on
Nginx:
pagespeed ReportUnloadTime on;
By default, the target of the asynchronous callback is the relative URL "/mod_pagespeed_beacon" (Apache) or "/ngx_pagespeed_beacon" (Nginx). PageSpeed handles these results itself and simply returns "204 No Content" which should cause minimal disruption for the client. It also records the number of callbacks, and the total of their onLoad times, in the statistics "page_load_count" and "total_page_load_ms". These are visible on the PageSpeed statistics page (which, by default, is only viewable from the server's localhost). You can divide total_page_load_ms by page_load_count to get the average onLoad time for your site.

You can change the location of the beacon to a different path (or even a different server) using the BeaconUrl directive. For example:

Apache:
ModPagespeedBeaconUrl "/my/path/to/beacon"
Nginx:
pagespeed BeaconUrl "/my/path/to/beacon";

Or, if you want to set up your own beacon server somewhere else, you can use:

Apache:
ModPagespeedBeaconUrl "http://my.other.server/my_beacon"
Nginx:
pagespeed BeaconUrl "http://my.other.server/my_beacon";

If you want to set an https beacon URL that is different from the http beacon URL (for example a different machine as shown below, or a non-default port), you can do so by separating the http and https URLs with a space. You don't need to do this if the beacon URLs only differ in protocol (http versus https):

Apache:
ModPagespeedBeaconUrl "http://insecure.server/my_beacon https://secure.server/my_beacon"
Nginx:
pagespeed BeaconUrl "http://insecure.server/my_beacon https://secure.server/my_beacon";

Note that the BeaconUrl used to require that custom beacon URLs end with "?ets=". Appending the query param to the custom URL is no longer required and should be removed if present.

If you want more detailed information about client load time (e.g. averages for different pages and different browsers, median times, historical behavior, etc.) you can get this information from your webserver logs. Every beacon load will be in your access.log along with the timestamp, referer, IP address, and user-agent—which tell you when, where, and by whom the onLoad measurement was taken. There are some existing tools to collect and analyze these data; one such example is Jiffy.

Requirements

The "Add Instrumentation" filter requires the "Add Head" filter, and will enable it automatically.

Risks

This filter could break pages if they include JavaScript which depends on knowing the first child of the <head> or the last child of the <body>.

The injected JavaScript is very lightweight, and should cause only a very tiny (though nonzero) amount of extra load on the client.

If you already have a client-side latency measurement system in place (such as Jiffy, Episodes, or Boomerang), you probably don't need this filter.

Note that the data reported by this filter is only approximate and does not include time the client spends resolving your domain, opening a connection, and waiting for the first few bytes of HTML.

References

  1. Zakas, Nicholas C., High Performance JavaScript. O'Reilly, 2010, p133.