PSR-7 Middleware for instrumenting PHP applications
PHP JavaScript
Permalink
Failed to load latest commit information.
src
tests Enable disablin fields by setting them to false Oct 8, 2016
.editorconfig First working concept Sep 30, 2016
.gitattributes Do not install codecov.yml Oct 9, 2016
.gitignore First working concept Sep 30, 2016
.travis.yml Test on PHP 7.1 Oct 9, 2016
CONTRIBUTING.md First working concept Sep 30, 2016
Gruntfile.js First working concept Sep 30, 2016
LICENSE.md First working concept Sep 30, 2016
README.md Enable running tests with composer test Oct 15, 2016
codecov.yml First working concept Sep 30, 2016
composer.json Enable running tests with composer test Oct 15, 2016
package.json First working concept Sep 30, 2016
phpunit.xml First working concept Sep 30, 2016

README.md

Instrument middleware

Latest Version Software License Build Status HHVM Status Coverage

Companion middleware for Instrument. Automates basic instrumenting of PSR-7 based application code.

Instrument Middleware

Install

Install using composer.

$ composer require tuupola/instrument-middleware

Usage

You must have access to InfluxDB database to store the data. Configure the Instrument instance and pass it to the middleware. This is the only mandatory parameter. Heads up! The order of middlewares is important. Instrument middleware must be the last one added.

require __DIR__ . "/vendor/autoload.php";

$app = new \Slim\App;

$influxdb = InfluxDB\Client::fromDSN("http+influxdb://foo:bar@localhost:8086/instrument");

$app->add(new Instrument\Middleware([
    "instrument" => new Instrument\Instrument([
        "adapter" => new Instrument\Adapter\InfluxDB($influxdb),
        "transformer" => new Instrument\Transformer\InfluxDB
    ])
]));

Or if you are using Slim 3 containers which is a bit cleaner.

require __DIR__ . "/vendor/autoload.php";

$app = new \Slim\App;
$container = $app->getContainer();

$container["influxdb"] = function ($container) {
    return InfluxDB\Client::fromDSN("http+influxdb://foo:bar@localhost:8086/instrument");
};

$container["instrument"] = function ($container) {
    return new Instrument\Instrument([
        "adapter" => new Instrument\Adapter\InfluxDB($container["influxdb"]),
        "transformer" => new Instrument\Transformer\InfluxDB
    ]);
};

$container["instrumentMiddleware"] = function ($container) {
    return new Instrument\Middleware([
        "instrument" => $container["instrument"]
    ]);
};

$app->add("instrumentMiddleware");

What is logged?

Let's assume you have the following routes.

$app->get("/", function ($request, $response, $arguments) {
    return $response->write("Here be dragons...\n");
});

$app->get("/hello/{name}", function ($request, $response, $arguments) {
    return $response->write("Hello {$arguments['name']}!\n");
});

When request is made the middleware saves basic instrumentation data to the database.

$ curl http://192.168.50.53/
Here be dragons...
$ curl http://192.168.50.53/hello/foo
Hello foo!
> select * from instrument
name: instrument
----------------
time                 bootstrap  memory   method  process  route       status  total
1475316633441185508  158        1048576  GET     53       /           200     213
1475316763025260932  140        1048576  GET     69       /hello/foo  200     211

Field bootstrap is the time elapsed between start of the request and executing the first middleware. Field total is the time elapsed between starting the request and exiting the last middleware. Note again that Instrument middleware must be the last one added so it will be executed first when entering and last when exiting the middleware stack.

Fields memory and process are the peak PHP memory usage and elapsed time during the processing of the request. This includes the route or controller and all other middlewares.

Tags method and status are the request method and the HTTP status code of the response. Tag route is the requested URI without query string.

Adding or overriding tags

You can add tags by using tags parameter. It can be either an array or anonymous function returning an array. Function receives both $request and $response objects as parameters. If you return any of the default tags it will override the value otherwise set by the middleware.

$app->add(new Instrument\Middleware([
    "instrument" => $instrument,
    "tags" => ["host" => "localhost", "method" => "XXX"]
]));

Is essentially the same as code below.

$app->add(new Instrument\Middleware([
    "instrument" => $instrument,
    "tags" => function ($request, $response) {
        return ["host" => "localhost", "method" => "XXX"];
    }
]));
> select * from instrument
name: instrument
----------------
time                 bootstrap  memory   host       method  process  route       status  total
1475316633441185508  158        1048576  localhost  XXX     53       /           200     213
1475316763025260932  140        1048576  localhost  XXX     69       /hello/foo  200     211

Customising field and tag names

All field and tag names can be customized. Following example changes all tag and field names. It also changes the measurement name. In InfluxDB lingo MEASUREMENT is the same as TABLE in SQL world.

$app->add(new Instrument\Middleware([
    "instrument" => $instrument,
    "measurement" = "api",
    "bootstrap" = "startup",
    "process" = "execution",
    "total" = "all",
    "memory" = "mem",
    "status" = "code",
    "route" = "uri",
    "method" = "verb"
]));
> select * from api
name: api
----------------
time                 startup  mem      verb  execution  uri         code  all
1475316633441185508  158      1048576  GET   53         /           200   213
1475316763025260932  140      1048576  GET   69         /hello/foo  200   211

To disable a tag or field set it to false.

$app->add(new Instrument\Middleware([
    "instrument" => $instrument,
    "measurement" = "api",
    "bootstrap" = "startup",
    "process" = false,
    "total" = "total",
    "memory" = false,
    "status" = false,
    "route" = false,
    "method" = false
]));
> select * from api
name: api
----------------
time                 startup  total
1475316633441185508  158      213
1475316763025260932  140      211

Manually adding data

You can also manually add additional data to the measurement.

$app->get("/manual", function ($request, $response, $arguments) {
    $timing = $this->instrument->timing("instrument");
    $timing->start("db");
    /* Some expensive database queries. */
    $timing->stop("db");
    return $response->write("Manually adding additional data...\n");
});
$ curl http://192.168.50.53/manual
Manually adding additional data...
> select * from instrument
name: instrument
----------------
time                 bootstrap  db   memory   method  process  route    status  total
1475318315949095876  155        411  1048576  GET     466      /manual  200     623

Testing

You can run tests either manually...

$ composer test

... or automatically on every code change.

$ npm install
$ grunt watch

Contributing

Please see CONTRIBUTING for details.

Security

If you discover any security related issues, please email tuupola@appelsiini.net instead of using the issue tracker.

License

The MIT License (MIT). Please see License File for more information.