Measure Server Timing Metrics with Server-Timing Header in SilverStripe

Posted 31 days ago by Jon Su

5 Minute(s) to read

Posted in

server side timings silverstripe banner image

In a world where the speed of web applications is imperative for user experience and Search Engine Optimisation (SEO) purposes, the ability to measure and identify potential performance bottlenecks is crucial.

There are many solutions already available to profile websites such as Blackfire and Xhprof, but these come with additional excessive overhead that is somewhat unnecessary. 

This is where the Server-Timing header can help developers identify and resolve bottlenecks to improve overall website performance.

What is the Server-Timing API?

The Server Timing API is a W3C supported specification which helps expose timing information from within an application to the front-end client-side. 

The Server-Timing API does not directly measure the timing of different server events but instead enables metrics to be surfaced to the browser, once the metrics themselves have been measured. 

The Time-To-First-Byte metric is commonly used to define how long the browser takes between making a request to the server and receiving a HTTP response. A number of different tasks are usually completed before a response is sent back to the client-side. Tasks such as reading data from a database, rendering the response in a template, or connecting to other APIS . In order to optimise this process, a developer needs to first identify where bottlenecks are occurring by measuring these tasks using either some 3rd-party tooling or via custom code. 

Once the event timings have been measured, the Server-Timing API communicates these back-end server-timing metrics to the client-side in a set of HTTP response headers. These headers can then be seen in either the developer tools of a user’s browser or retrieved via the PerformanceServerTiming interface. 

Server-timing metrics communicated by the Server-Timing header can be in different formats including:

  • metric name only
  • metric with value
  • metric with value and description
  • metric with description:

The following shows an example of how different server metrics can be formatted:

 


// Single metric without value

Server-Timing: missedCache

// Single metric (called cpu) with value

Server-Timing: cpu;dur=2.4

// Single metric (metric is called cache)with description and value

Server-Timing: cache;desc="Cache Read";dur=23.2
 

// Two metrics with value - db and app

Server-Timing: db;dur=53, app;dur=47.2
 

--- response body ---

// Overall response time

Server-Timing: total;dur=123.4


The Internetrix Server Timing module for SilverStripe Framework

Internetrix has built an open-source Server Timing module for SilverStripe Framework which acts as a middleware for developers to easily measure custom timing metrics and add them to the headers of a response. 

The module can simply be installed via Composer:


composer require internetrix/silverstripe-server-timing

When configuring the module's middleware, the  \Internetrix\Control\Middleware\ServerTimingMiddleware::class should be added to the top of the SilverStripe Framework middleware execution pipeline before the SilverStripe Framework has been fully bootstrapped, to allow for the most accurate measuring of server-timings.

The instructions to do so can be found in the Configuration section of the module’s README file.

Default timing metrics

Once the module has been installed and configured, the web application will automatically start measuring the following metrics out-of-the-box and add them to the Server-Timing header for all HTTP responses:

  • Bootstrap: Time until it takes for the ServerTimingMiddleware to be called
  • Application: Time it takes to receive a response from the SilverStripe Franemwork application.
  • Total: The total duration being a request being sent to the server until right before a response being sent back to the client-side.

The following shows how the default server-timing metrics appears in the developer tools of Chrome when selecting the web request for the current page in the NETWORK tab:

Custom timing metrics

If you're looking to go above and beyond the default metrics, you can easily add more server-side timing metrics to understand what is slowing down the response from the server. 

You will first need to identify 2 points in the custom code where you would like to measure the timing for (i.e the time it takes from the 1st point to the 2nd point).

Once you have identified the section of the application that they would like to measure, you will need to call the start() and end() methods on the Internetrix\ServerTiming\Helper\ServerTiming::class:


ServerTiming::start('Custom Metric to Measure');

// do something in the code

ServerTiming::end('Custom Metric to Measure');

Alternatively, you can also measure the duration it took from the start of the request to reach a certain point in the application’s execution by calling the addMetric() method on the same ServerTiming class:


ServerTiming::addMetric('Metric to Add');

Client-Side Analytics Integration

While the server-timing header information appears in the developer tools for browsers, it would be convenient if there was a way that this information could be read within a third-party analytics tool such as Google Analytics. Luckily, this is where the PerformanceServerTiming interface comes into play. Using the PerformanceServerTiming interface, we can use JavaScript to collect and send the metrics to some third-party analytics tools. 

The following is a simple example of JavaScript implementation to push server-timing metrics to a JavaScript object called serverTimings, ready to be used and integrated with any 3rd-party analytics platform:

 


try {
  serverTimings = {};

  // Create the performance observer.
  const po = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      // Logs each server timing data for this response to object
      if ('serverTiming' in entry) {
        entry.serverTiming.forEach(function (timing) {
          let name = timing.name;
          serverTimings[name] = timing.duration;
        });
      }
    }
  });

  // Start listening for `navigation` entries to be dispatched.
  po.observe({ type: 'navigation', buffered: true });

// This is the JavaScript object with all the metrics stored inside it:    
  console.log(serverTimings);

} catch (e) {
  // Do nothing if API is not support by browser
}

Limitations

It is important to note when using the PerformanceServerTiming interface that it is restricted to the same origin and only available in secure contexts (HTTPS) for some browsers. This means that server-timing information will not be retrievable using the PerformanceServerTiming interface on any environments without SSL. The server-timing information will still however be visible using the developer tools of a browser.

Try out the Server-Timing module now

You can try out the new Internetrix Server-Timing module for SilverStripe Framework today by checking it out on Github. If you have any questions or need support implementing the module, let us know!