Development notes

itsgoingd edited this page Jan 5, 2014 · 2 revisions

Development notes

This document provides a quick overview of Clockwork architecture, useful when implementing support for new frameworks. If you have any questions please contact me via email or twitter or open a new issue on github. Current version of Clockwork contains ootb support for Laravel 4 framework, I recommend you to use this implementation as example when adding new frameworks.

For a step-by-step example of using Clockwork in a custom application check out this blogpost - http://hotcashew.com/2013/12/integrating-chromes-clockwork-custom-web-app/

Chrome Extension

The Chrome extension checks HTTP responses for X-Clockwork-Version and X-Clockwork-Id headers. X-Clockwork-Version contains the version of Clockwork server-side component, this allows to keep the extension compatible with older versions even when the data protocol changes. When X-Clockwork-Version is found in response headers, HTTP GET ajax request is made to /__clockwork/{id}, where {id} is the value of X-Clockwork-Id header. JSON encoded data of the request with specified id (output of Clockwork\Request\Request::toJson) is returned and the displayed in the extension.

Server-side library

Server-side library is provided to make adding support for various frameworks and applications as simple as possible. It consists of several components: DataSources - collect the data, Request - represents the collected data, Storage - stores and retrieves requests, Support - framework-specific support files like service providers, middlewares etc.

Typical use looks like this:

  • main Clockwork class is instantiated, automatically creating new Request\Request instance
  • data sources are added via $clockwork->addDataSource method
  • storage instance is set via $clockwork->setStorage method
  • application runs
  • $clockwork->resolveRequest is called - Request instance is passed through resolve method of each DataSource, each adding relevant data to the Request instance
  • $clockwork->storeRequest is called - Request instance is stored via store method on the set Storage instance
  • value of Clockwork::VERSION is sent as X-Clockwork-Version header and value of $clockwork->getRequest()->id is sent as X-Clockwork-Id header

When /__clockwork/{id} is requested:

  • Storage instance is created
  • request is retrieved via $storage->retrieve($id)
  • JSON encoded data is outputed via $request->toJson() method

Collected data is stored in the Request object simply via assignment to public attributes, there is no validation done in Request class, DataSource implementation is responsible for assigning only valid values.

Available attibutes:

  • id - unique request id, should never be set manually, is automatically generated from current time and random number when new instance of Request is created
  • time - request time, expects numeric value in microseconds
  • method - used HTTP method, expects string value
  • uri - request URI, expects string value
  • headers - request headers, expects array in the form of:
array(
   'string value of header name' => array('string value of header value', ...),
    ...
)
  • controller - used controller name in case of MVC framework, expects string value
  • getData - request GET data, expects array of get data
  • postData - request POST data, expects array of post data
  • sessionData - session data, expects array of session data
  • cookies - request cookies, expects array of cookies data
  • responseTime - response time, expects numeric value in microseconds when response was sent, response duration is then computed as time - responseTime
  • responseStatus - response HTTP status code, expects numeric value
  • databaseQueries - database query data, including durations, expects array in the form of:
array(
    array(
        'query'
 => 'string value of first sql query in executable form',
        'duration' => 'numeric value in miliseconds'
    ),
    ...
)
  • timelineData - data for timeline tab, expects array in the form of:
array(
    array(
        'start' => 'numeric representation of events start time in microseconds',
        'end' => 'numeric representation of events end time in microseconds',
        'duration' => 'numeric representation of events duration in milliseconds',
        'description' => 'string value describing the event'
    ),
    ...
)
  • there is a helper object for creating timeline data available - Request\Timeline
  • log - application log, expects array in form of:
array(
    array(
        'time' => 'numeric value of timestamp in seconds',
        'level' => 'numeric constant representing message type',
        'message' => 'string value of message'
    ),
    ...
)
  • available log levels are: 1 - DEBUG, 2 - INFO, 3 - NOTICE, 4 - WARNING, 5 - ERROR
  • there is a helper object for creating log data available - Request\Log
  • routes - application routes, expects array of routes in form of:
array(
    array(
        'uri' => 'string value of routes uri',
        'name' => 'string value of routes name',
        'action' => 'string value of routes callback',
        'before' => 'string value of route's before filters',
        'after' => 'string value of route's after filter'
    ),
    ...
)

All attributes can only contain data that can be encoded as JSON (no resources, object instances, closures), as this is the format used for storing and transfering the data to the extension.

Note that the extension does not require you to use this library, you can provide your custom implementation that returns correct JSON data, but it is recommended as this library provides somewhat stable API, allowing to the data format to change.

Sample of correct JSON response:

{
    "id":"1379711612.4843.251523074",
    "time":1379711611.8899,
    "method":"GET",
    "uri":"\/users",
    "headers": {
        "host":["base.daylight.home"],
        "x-real-ip":["10.10.10.103"],
        "connection":["close"],
        "accept":["*\/*"],
        "user-agent":["Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/31.0.1637.0 Safari\/537.36"],
        "accept-encoding":["gzip,deflate,sdch"],
        "accept-language":["en-US,en;q=0.8,cs;q=0.6,sk;q=0.4"],
        "cookie":["laravel_session=s566k2i47igcam0v6nn8i0f4h5"]
    },
    "controller":"UsersController@index",
    "getData":[],
    "postData":[],
    "sessionData": {
        "_sf2_attributes": {
            "_token":"ZI3OJ0EliZieVvp9ol3hveu11NqMjSr6MXLe3tpF"
        }
    },
    "cookies": {
        "laravel_session":"s566k2i47igcam0v6nn8i0f4h5"
    },
    "responseTime":1379711612.5321,
    "responseStatus":200,
    "responseDuration":642.18783378601,
    "databaseQueries": [
        {
            "query":"SELECT count(*) as aggregate FROM `languages` WHERE `id` = 'en'",
            "duration":0.75
        },
        {
            "query":"SELECT * FROM `languages`",
            "duration":0.52
        }
    ],
    "databaseDuration":1.27,
    "timelineData": {
        "total": {
            "start":1379711611.8899,
            "end":1379711612.5323,
            "duration":642.45295524597,
            "description":"Total execution time."
        },
        "initialisation": {
            "start":1379711611.8899,
            "end":1379711612.4734,
            "duration":583.5108757019,
            "description":"Application initialisation."
        },
        "run": {
            "start":1379711612.4734,
            "end":1379711612.5323,
            "duration":58.935880661011,
            "description":"Framework running."
        },
        "boot": {
            "start":1379711612.4734,
            "end":1379711612.4743,
            "duration":0.86092948913574,
            "description":"Framework booting."
        },
        "dispatch": {
            "start":1379711612.4833,
            "end":1379711612.5318,
            "duration":48.506021499634,
            "description":"Router dispatch."
        }
    },
    "log":[],
    "routes":null,
    "userData":null
}
Clone this wiki locally
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.