Skip to content

Commit

Permalink
Trace: Updated Tracing & Added Documentation #25
Browse files Browse the repository at this point in the history
  • Loading branch information
iamacarpet committed Sep 5, 2018
1 parent 7e43690 commit 57bdec5
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 21 deletions.
110 changes: 105 additions & 5 deletions docs/trace.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,125 @@
## StackDriver Trace Support
# StackDriver Trace Integration

This package includes built in support for tracing important components to StackDriver Trace.

By default, this includes:
* Laravel startup
* (currently doesn't render in the tree view properly)
* Laravel internals (including more granular startup).
* Application construct
* Request capture
* Request handle
* Response send
* Request terminate (cleanup)
* Application specific
* Middleware
* (soon) Middleware
* Time in Router
* Controller / Route-Closure Runtime
* Blade view compile/render
* External calls / RPC
* memcached
* redis
* (soon) redis
* MySQL
* PDO
* Eloquent (Laravel)
* Datastore
* Guzzle (HTTP(s))
* (soon) Datastore
* (soon) Guzzle (HTTP(s))

It also allows you to register your own trace providers to be registered as the application boots, via the config file for this package (`trace_providers` in `gaesupport.php`).

## Architecture
There are two different levels of trace integration:

* Low Level (to catch Laravel core boot)
* Higher level (via Service Provider)

All of the trace providers at all levels should implement the interface `OpenCensus\Trace\Integrations\IntegrationInterface`, which providers a static `load` method where the trace hooks are registered.

### Low level
To allow us to capture the startup of Laravel in more detail and to make sure the core is ready to be traced before any of it runs, we set up the OpenCensus trace library and register some trace hooks before loading Laravel.

We do this by asking composer to include `src/preload.php` once it has set up the autoloader, the same way helper functions are initialised.

`src/preload.php` will first include `src/helpers.php` to register our helper functions (which is done because composer can't guarantee load order if we specify an array of files to load in `composer.json` and we need our helper functions before the preload functions run).

By default, the list of low level providers is provided by calling `A1comms\GaeSupportLaravel\Trace\LowLevelLoader`, but this can be overridden by creating your own `LowLevelLoader` that implements `A1comms\GaeSupportLaravel\Trace\LowLevelLoaderInterface` at `App\Trace\LowLevelLoader`, which we check for before loading the default.

Example `app/Trace/LowLevelLoader.php` that just loads `Memcached` tracing:

```php
<?php

namespace App\Trace;

use A1comms\GaeSupportLaravel\Trace\LowLevelLoaderInterface;

class LowLevelLoader implements LowLevelLoaderInterface
{
public static function getList()
{
return [
OpenCensus\Trace\Integrations\Memcached::class,
];
}
}
```

### Higher Level
For trace providers that can be registered after Laravel has loaded and during the service provider boot, when other service providers may already be running, we can register them via the `TraceServiceProvider` and have Laravel functionality available if required.

Firstly, the `TraceServiceProvider` will register an event for `laravel/boostrap`, with a start time set to `LARAVEL_START` as set in the top of `public/index.php`, to document the point in execution when it was run, as an indication of when Laravel finished loading.

Next, it will look in the application config for our service (`gaesupport.php`) and run the `load` function for any classes listed in the `trace_providers` array, allowing you to add trace hooks for your own application.

As an example, to trace the `enqueue` function of a model class `CustomQueue`, I'd start by creating the file `app/Trace/CustomQueueModel.php` with the contents:

```php
<?php

namespace App\Trace;

use OpenCensus\Trace\Integrations\IntegrationInterface;
use App\CustomQueue;

class CustomQueueModel implements IntegrationInterface
{
public static function load()
{
if (!extension_loaded('opencensus')) {
trigger_error('opencensus extension required to load Laravel integrations.', E_USER_WARNING);
return;
}

opencensus_trace_method(CustomQueue::class, 'enqueue', [self::class, 'handleEnqueue']);
}

public static function handleResponseSend($scope, $job)
{
return [
'name' => 'model/CustomQueue/enqueue',
'attributes' => [
'job' => $job,
]
];
}
}
```

To note here is the importance of the parameters passed to the callback function, with the first being the instance of the class as it was called, then all of the parameters to the function as it was called.

For more information, see the OpenCensus PECL extension documentation.

https://github.com/census-instrumentation/opencensus-php/blob/master/docs/content/using-the-extension.md

Next, update your `gaesupport.php` configuration file to include that trace provider into the array:

```php
'trace_providers' => [
App\Trace\CustomQueueModel:class,
],
```

## Installation
Since the low level trace setup is done as part of the composer autoloader initialisation, most of the installation is taken care of once you've installed the package, although for the higher level & custom trace providers, you'll need to make sure the `GaeSupportServiceProvider` and `TraceServiceProvider` are both loaded into Laravel.

In Laravel 5.5, service providers are automatically discovered by default, so unless you've disabled this functionality, you shouldn't need to do anything else either.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace A1comms\GaeSupportLaravel\Trace\Integration;
namespace A1comms\GaeSupportLaravel\Trace\Integration\LowLevel;

use OpenCensus\Trace\Integrations\IntegrationInterface;
use Illuminate\Foundation\Application as LaravelApplication;
Expand Down Expand Up @@ -35,7 +35,7 @@ public static function load()
opencensus_trace_method(LaravelResponse::class, 'send', [self::class, 'handleResponseSend']);
// TODO: Eventually we want to be able to remove this,
// the "send" method in "LaravelResponse" is inherited from "BaseResponse"
// but unfortunately the opensensus extension doesn't trigger for inherited methods.
// but unfortunately the OpenCensus extension doesn't trigger for inherited methods.
// https://github.com/census-instrumentation/opencensus-php/issues/201
opencensus_trace_method(BaseResponse::class, 'send', [self::class, 'handleResponseSend']);

Expand Down
28 changes: 28 additions & 0 deletions src/A1comms/GaeSupportLaravel/Trace/LowLevelLoader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace A1comms\GaeSupportLaravel\Trace;

/**
* Class to return the low level trace modules to load.
*/
class LowLevelLoader implements LowLevelLoaderInterface
{
/**
* Static method to get the list of trace modules to load.
*/
public static function getList()
{
return [
// OpenCensus provides a basic Laravel trace adapter,
// which covered Eloquent and view compilation.
OpenCensus\Trace\Integrations\Laravel::class,
// Also load our own extended Laravel trace set.
A1comms\GaeSupportLaravel\Trace\Integration\LowLevel\LaravelExtended::class,
// Trace our other basic functions...
OpenCensus\Trace\Integrations\Mysql::class,
OpenCensus\Trace\Integrations\PDO::class,
OpenCensus\Trace\Integrations\Memcached::class,
];
}
}
17 changes: 17 additions & 0 deletions src/A1comms/GaeSupportLaravel/Trace/LowLevelLoaderInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace A1comms\GaeSupportLaravel\Trace;

/**
* Interface to implement for loading your own list
* of low level trace modules.
*/
interface LowLevelLoaderInterface
{
/**
* Static method to get the list of trace modules to load.
*
* For an example, see the default at LowLevelLoader.
*/
public static function getList();
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public function boot()

// Create a span that starts from when Laravel first boots (public/index.php)
// ---
// TODO: Set parentSpanId to the rootSpan->spanId() from OpenSensus,
// TODO: Set parentSpanId to the rootSpan->spanId() from OpenCensus,
// to help it merge properly in the tree view.
// Need to wait for rootSpan visibility to be changed to public.
// https://github.com/census-instrumentation/opencensus-php/issues/199
Expand Down
2 changes: 1 addition & 1 deletion src/config/gaesupport.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
|--------------------------------------------------------------------------
|
| This is an array of classes to register for the StackDriver Trace integration,
| defining runtime trace hooks via the opensensus PECL module,
| defining runtime trace hooks via the OpenCensus PECL module,
| so code changes inside the application aren't required.
|
| These should implement OpenCensus\Trace\Integrations\IntegrationInterface
Expand Down
19 changes: 7 additions & 12 deletions src/preload.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,13 @@
Tracer::start(new StackdriverExporter());
}

// TODO: Different arrays for Laravel vs Lumen?
$traceProviders = [
// OpenSensus provides a basic Laravel trace adapter,
// which covered Eloquent and view compilation.
OpenCensus\Trace\Integrations\Laravel::class,
// Also load our own extended Laravel trace set.
A1comms\GaeSupportLaravel\Trace\Integration\LaravelExtended::class,
// Trace our other basic functions...
OpenCensus\Trace\Integrations\Mysql::class,
OpenCensus\Trace\Integrations\PDO::class,
OpenCensus\Trace\Integrations\Memcached::class,
];
$loaderInterface = 'App\\Trace\\LowLevelLoader';
if (!class_exists($loaderInterface))
{
// TODO: Different default arrays for Laravel vs Lumen?
$loaderInterface = A1comms\GaeSupportLaravel\Trace\LowLevelLoader::class;
}
$traceProviders = $loaderInterface::getList();

foreach ($traceProviders as $p) {
$p::load();
Expand Down

0 comments on commit 57bdec5

Please sign in to comment.