Integrating Chrome’s Clockwork into your web-app

Say hello to the Clockwork tab in your Dev Tools

Clockwork is awesome; and it can work with any server-side platform/framework.  I’ll walk you through integrating it into your PHP web-app.

Part 1: Install

Install using Composer:

  • Add “itsgoingd/clockwork”: “dev-master” to your composer.json
  • Run composer.phar update

Alternatively, you can clone https://github.com/itsgoingd/clockwork/ and load the library the old fashioned way.

Part 2: Integrate into your web-app

Best place to put this is in the root of your app, around the controller/router. The header() commands need to run before anything else is sent out, they will tell Chrome’s Clockwork tab what file to request from the server to get stats.

The last two lines resolveRequest() and storeRequest()  must run after your web-app is done doing all of its great stuff. The resolve()  method will be called on each provided DataSource. This will parse and save all your timings, logs, queries, etc for Clockwork to fetch later.

// this sends out headers, so unless you are buffering output
// you may want to place this early in your app
$clockwork = new Clockwork();
header("X-Clockwork-Id: " . $clockwork->getRequest()->id);
header("X-Clockwork-Version: " . Clockwork::VERSION);

// spawn your own custom DataSource (explained below)
$clockwork->addDataSource(new YourCustomDataSource($yourAppContext));

// attach a sample datasource that comes with
// the Clockwork library (grabs session info, etc)
$clockwork->addDataSource(new PhpDataSource());

// we could write a custom APC, MemCached, etc here
// or just use the FileStorage that comes with Clockwork
$clockwork->setStorage(new FileStorage("some/path/"));

// run your web-app here
// ...

// once your app is done, tell Clockwork to resolve and store
// data in a file on your server; it will call the resolve()
// method on YourCustomDataSource and PhpDataSource
$clockwork->resolveRequest();
$clockwork->storeRequest();

This creates a new file in “some/path/”.

After the page loads, Chrome’s Clockwork extension pickups the X-Clockwork headers, and send a new request to your server, asking to fetch the generated file using X-Clockwork-Id.

e.g.: If the X-Clockwork-Id is “1387208177.8923.1394938488” then Chrome’s Clockwork extension will send a request to your server with the URI: /__clockwork/1387208177.8923.1394938488

You can handle this request by listening for calls to “/__clockwork/[*:id]” and doing something like this:

$storage = new FileStorage("/some/path/");
$data = $storage->retrieve($ctx->parameters->id);
$data->toJson();

Routers like Klein or AltoRouter can take above syntax and return the request-id. Or you can parse $_SERVER[‘REQUEST_URI’] manually to grab the id. FileStorage does the rest for you :)

Part 3: Custom Data Source

class YourCustomDataSource extends DataSource
{
	protected $context;

	/**
	 * YourAppContext should link to your main web-app
         * and fetch timings, queries, and logs
	 */
	function __construct(YourAppContext $context)
	{
		$this->context = $context;
	}

	/**
	 * the entry-point. called by Clockwork itself.
	 */
	function resolve(Request $request)
	{
		$timings = $this->context->getTimings();

		// optionally: pre-sort the timeline
		uasort($timeline, function($a, $b) {
			if($a['start'] > $b['start'])
				return 1;

			if($a['start'] == $b['start']) {
				if($a['end'] > $b['end'])
					return 1;
				elseif ($a['end'] < $b['end'])
					return -1;

				return 0;
			}

			return -1;
		});

		$queries = $this->context->getQueries();

		$request->timelineData = $timeline;
		$request->databaseQueries = $queries;

		return $request;
	}
}

e.g.:

$timings[0] = ['start' => 1387208058.1, 'end' => 1387208058.5, 'duration' => 40, 'description' => 'parsing tweets']

The start & end are in seconds, and duration is in milliseconds. Start & end should also be relative to ($_SERVER[‘REQUEST_TIME_FLOAT’] * 1000).  So provide absolute timestamps that you get from microtime(true) rather than your own timestamps that start at 0.

$queries[0] = ['query' => "SELECT awesomeness FROM cereals WHERE name = `Captain Crunch`", 'duration' => 13]

Here query can be either a proper SQL query or just a label, duration is in milliseconds.

Tada :)

References:

https://github.com/itsgoingd/clockwork/wiki/Development-notes

 

Leave a Reply

Your email address will not be published. Required fields are marked *