Skip to content

Commit e83d65a

Browse files
committed
Trace: Guzzle Integration #25
1 parent 57bdec5 commit e83d65a

File tree

6 files changed

+143
-3
lines changed

6 files changed

+143
-3
lines changed

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
],
1111
"require": {
1212
"opencensus/opencensus-exporter-stackdriver": "^0.1.0",
13-
"google/cloud-logging": "^1.12"
13+
"google/cloud-logging": "^1.12",
14+
"guzzlehttp/guzzle": "~6"
1415
},
1516
"require-dev": {
1617
},

docs/trace.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ By default, this includes:
2323
* PDO
2424
* Eloquent (Laravel)
2525
* (soon) Datastore
26-
* (soon) Guzzle (HTTP(s))
26+
* Guzzle (HTTP(s))
2727

2828
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`).
2929

@@ -122,4 +122,9 @@ Next, update your `gaesupport.php` configuration file to include that trace prov
122122
## Installation
123123
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.
124124

125-
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.
125+
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.
126+
127+
## Guzzle Sub-Request Trace Merging
128+
StackDriver Trace has the ability to show trace points from sub-requests within the same project (although it can be to other App Engine services) into the same trace entry in the GUI, allowing you to view the aggregate impact of a whole request in a micro-service environment.
129+
130+
To take advantage of this, replace your `GuzzleHttp\Client` with `A1comms\GaeSupportLaravel\Integration\Guzzle\Client`.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
namespace A1comms\GaeSupportLaravel\Integration\Guzzle;
4+
5+
use GuzzleHttp\Client as GuzzleClient;
6+
use A1comms\GaeSupportLaravel\Trace\Integration\Guzzle\Middleware as TraceMiddleware;
7+
use Illuminate\Support\Facades\Log;
8+
9+
/**
10+
* Extension to Guzzle Client to apply relevant middleware & config.
11+
*/
12+
class Client extends GuzzleClient
13+
{
14+
/**
15+
* e.g.
16+
* $client = new Client([
17+
* ...standard guzzle config...,
18+
* 'gaesupport' => [
19+
* 'trace' => false,
20+
* 'auth' => [
21+
* ...auth config placeholder...,
22+
* ],
23+
* ],
24+
* ]);
25+
*/
26+
public function __construct(array $config = [])
27+
{
28+
if ((!is_gae()) || (php_sapi_name() == 'cli')) {
29+
return parent::__construct($config);
30+
}
31+
32+
if (!isset($config['handler']))
33+
{
34+
$config['handler'] = HandlerStack::create();
35+
$config['handler']->setHandler(\GuzzleHttp\choose_handler());
36+
}
37+
38+
if (method_exists($config['handler'], 'push'))
39+
{
40+
// We are able to modify the handler stack, continue...
41+
42+
// Unless disabled, add TraceMiddleware for sub-request trace merging in StackDriver.
43+
if (@$config['gaesupport']['trace'] !== false)
44+
{
45+
$config['handler']->push(new TraceMiddleware());
46+
}
47+
}
48+
else
49+
{
50+
Log::warning('A1comms\\GaeSupportLaravel\\Integration\\Guzzle\\Client: Unable to modify handler stack, no push method defined');
51+
}
52+
53+
return parent::__construct($config);
54+
}
55+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
namespace A1comms\GaeSupportLaravel\Trace\Integration\Guzzle;
4+
5+
use OpenCensus\Trace\Tracer;
6+
use OpenCensus\Trace\Propagator\HttpHeaderPropagator;
7+
use OpenCensus\Trace\Propagator\PropagatorInterface;
8+
use Psr\Http\Message\RequestInterface;
9+
10+
class Middleware
11+
{
12+
/**
13+
* @var PropagatorInterface
14+
*/
15+
private $propagator;
16+
17+
/**
18+
* Create a new Guzzle middleware that creates trace spans and propagates the current
19+
* trace context to the downstream request.
20+
*
21+
* @param PropagatorInterface $propagator Interface responsible for serializing trace context
22+
*/
23+
public function __construct(PropagatorInterface $propagator = null)
24+
{
25+
$this->propagator = $propagator ?: new HttpHeaderPropagator();
26+
}
27+
28+
/**
29+
* Magic method which makes this object callable. Guzzle middleware are expected to be
30+
* callables.
31+
*
32+
* @param callable $handler The next handler in the HandlerStack
33+
* @return callable
34+
*/
35+
public function __invoke(callable $handler)
36+
{
37+
return function (RequestInterface $request, $options) use ($handler) {
38+
$context = Tracer::spanContext();
39+
if ($context->enabled()) {
40+
$request = $request->withHeader(
41+
$this->propagator->key(),
42+
$this->propagator->formatter()->serialize($context)
43+
);
44+
}
45+
return $handler($request, $options);
46+
};
47+
}
48+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
namespace A1comms\GaeSupportLaravel\Trace\Integration\Guzzle;
4+
5+
use OpenCensus\Trace\Integrations\IntegrationInterface;
6+
use GuzzleHttp\Client as GuzzleClient;
7+
8+
class TraceProvider implements IntegrationInterface
9+
{
10+
public static function load()
11+
{
12+
if (!extension_loaded('opencensus')) {
13+
trigger_error('opencensus extension required to load Laravel integrations.', E_USER_WARNING);
14+
return;
15+
}
16+
17+
opencensus_trace_method(GuzzleClient::class, '__call', [self::class, 'handleRequest']);
18+
}
19+
20+
public static function handleRequest($scope, $method, $args)
21+
{
22+
return [
23+
'name' => 'GuzzleHttp::request',
24+
'attributes' => [
25+
'method' => $method,
26+
'uri' => (count($args) < 1) ? 'invalid' : $args[0],
27+
],
28+
];
29+
}
30+
}

src/A1comms/GaeSupportLaravel/Trace/LowLevelLoader.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public static function getList()
2323
OpenCensus\Trace\Integrations\Mysql::class,
2424
OpenCensus\Trace\Integrations\PDO::class,
2525
OpenCensus\Trace\Integrations\Memcached::class,
26+
A1comms\GaeSupportLaravel\Trace\Integration\Guzzle\TraceProvider::class,
2627
];
2728
}
2829
}

0 commit comments

Comments
 (0)