Skip to content
This repository was archived by the owner on Oct 3, 2023. It is now read-only.

Commit dc443e4

Browse files
authored
Binary propagation (#30)
* Add test for binary propagation * Implement the binary propagator * Fix Guzzle integrations to accept a propagator interface * Fix binary propagator to use the OPTION_ENABLED constant instead of a hard coded int * Split PropagatorInterface with FormatterInterface * Fix grpc and Guzzle integrations to use the new PropagatorInterface * Fix alias for the Guzzle integrations * Update docs * Fix CS * Add example for propagating TraceContext over Grpc * Add tests for PropagatorInterface inject * Fix grammar in docs * Add test for when an invalid format is attempted deserialization * Catch a bad binary format, trigger a warning and return an empty TraceContext
1 parent b4c2103 commit dc443e4

14 files changed

Lines changed: 567 additions & 107 deletions

src/Trace/Integrations/Grpc.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
namespace OpenCensus\Trace\Integrations;
1919

2020
use Grpc\BaseStub;
21+
use OpenCensus\Trace\RequestTracer;
22+
use OpenCensus\Trace\Propagator\GrpcMetadataPropagator;
2123

2224
/**
2325
* This class handles instrumenting grpc requests using the opencensus extension.
@@ -88,4 +90,35 @@ public static function load()
8890
];
8991
});
9092
}
93+
94+
/**
95+
* Update metadata handler for grpc clients.
96+
*
97+
* Example:
98+
*
99+
* ```
100+
* use OpenCensus\Trace\Integrations\Grpc;
101+
*
102+
* # MathClient is the generated client from the proto definition
103+
* $client = new Math\MathClient($host, ['update_metadata' => [Grpc::class, 'updateMetadata']]);
104+
*
105+
* # The call will pass the current trace context using the GrpcMetadataPropagator and BinaryFormatter
106+
* $call = $client->Div($divArgs);
107+
* $response = $call->wait();
108+
* ```
109+
*
110+
* @param array $metadata
111+
* @param string $jwtAuthUri
112+
* @return array
113+
*/
114+
public static function updateMetadata($metadata, $jwtAuthUri)
115+
{
116+
if ($context = RequestTracer::context()) {
117+
$propagator = new GrpcMetadataPropagator();
118+
$metadata += [
119+
$propagator->key() => $propagator->formatter()->serialize($context)
120+
];
121+
}
122+
return $metadata;
123+
}
91124
}

src/Trace/Integrations/Guzzle/EventSubscriber.php

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
namespace OpenCensus\Trace\Integrations\Guzzle;
1919

2020
use OpenCensus\Trace\RequestTracer;
21+
use OpenCensus\Trace\Propagator\HttpHeaderPropagator;
22+
use OpenCensus\Trace\Propagator\PropagatorInterface;
2123
use GuzzleHttp\Event\BeforeEvent;
2224
use GuzzleHttp\Event\EndEvent;
2325
use GuzzleHttp\Event\SubscriberInterface;
@@ -39,7 +41,21 @@
3941
*/
4042
class EventSubscriber implements SubscriberInterface
4143
{
42-
const TRACE_CONTEXT_HEADER = 'X-Cloud-Trace-Context';
44+
/**
45+
* @var PropagatorInterface
46+
*/
47+
private $propagator;
48+
49+
/**
50+
* Create a new Guzzle event listener that creates trace spans and propagates the current
51+
* trace context to the downstream request.
52+
*
53+
* @param PropagatorInterface $propagator Interface responsible for serializing trace context
54+
*/
55+
public function __construct(PropagatorInterface $propagator = null)
56+
{
57+
$this->propagator = $propagator ?: new HttpHeaderPropagator();
58+
}
4359

4460
/**
4561
* Returns an array of event names this subscriber wants to listen to.
@@ -64,7 +80,7 @@ public function onBefore(BeforeEvent $event)
6480
{
6581
$request = $event->getRequest();
6682
if ($context = RequestTracer::context()) {
67-
$request->setHeader(self::TRACE_CONTEXT_HEADER, $context);
83+
$request->setHeader($this->propagator->key(), $this->propagator->formatter->serialize($context));
6884
}
6985
RequestTracer::startSpan([
7086
'name' => 'GuzzleHttp::request',

src/Trace/Integrations/Guzzle/Middleware.php

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
namespace OpenCensus\Trace\Integrations\Guzzle;
1919

2020
use OpenCensus\Trace\RequestTracer;
21+
use OpenCensus\Trace\Propagator\HttpHeaderPropagator;
22+
use OpenCensus\Trace\Propagator\PropagatorInterface;
2123
use Psr\Http\Message\RequestInterface;
2224

2325
/**
@@ -39,7 +41,23 @@
3941
*/
4042
class Middleware
4143
{
42-
const TRACE_CONTEXT_HEADER = 'X-Cloud-Trace-Context';
44+
const DEFAULT_HEADER_NAME = 'X-Cloud-Trace-Context';
45+
46+
/**
47+
* @var PropagatorInterface
48+
*/
49+
private $propagator;
50+
51+
/**
52+
* Create a new Guzzle middleware that creates trace spans and propagates the current
53+
* trace context to the downstream request.
54+
*
55+
* @param PropagatorInterface $propagator Interface responsible for serializing trace context
56+
*/
57+
public function __construct(PropagatorInterface $propagator = null)
58+
{
59+
$this->propagator = $propagator ?: new HttpHeaderPropagator();
60+
}
4361

4462
/**
4563
* Magic method which makes this object callable. Guzzle middleware are expected to be
@@ -52,7 +70,10 @@ public function __invoke(callable $handler)
5270
{
5371
return function (RequestInterface $request, $options) use ($handler) {
5472
if ($context = RequestTracer::context()) {
55-
$request = $request->withHeader(self::TRACE_CONTEXT_HEADER, $context);
73+
$request = $request->withHeader(
74+
$this->propagator->key(),
75+
$this->propagator->formatter()->serialize($context)
76+
);
5677
}
5778
return RequestTracer::inSpan([
5879
'name' => 'GuzzleHttp::request',
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
/**
3+
* Copyright 2017 OpenCensus Authors
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
namespace OpenCensus\Trace\Propagator;
19+
20+
use OpenCensus\Trace\TraceContext;
21+
22+
/**
23+
* This propagator contains the method for serializaing and deserializing
24+
* TraceContext over a binary format.
25+
*
26+
* See https://github.com/census-instrumentation/opencensus-specs/blob/master/encodings/BinaryEncoding.md
27+
* for the encoding specification.
28+
*/
29+
class BinaryFormatter implements FormatterInterface
30+
{
31+
const OPTION_ENABLED = 1;
32+
33+
/**
34+
* Generate a TraceContext object from the Trace Context header
35+
*
36+
* @param string $header
37+
* @return TraceContext
38+
*/
39+
public function deserialize($bin)
40+
{
41+
$data = @unpack('Cversion/Cfield0/H32traceId/Cfield1/H16spanId/Cfield2/Coptions', $bin);
42+
if ($data === false) {
43+
trigger_error('Invalid binary format for TraceContext', E_USER_WARNING);
44+
return new TraceContext();
45+
}
46+
$enabled = !!($data['options'] & self::OPTION_ENABLED);
47+
return new TraceContext($data['traceId'], hexdec($data['spanId']), $enabled, true);
48+
}
49+
50+
/**
51+
* Convert a TraceContext to header string
52+
*
53+
* @param TraceContext $context
54+
* @return string
55+
*/
56+
public function serialize(TraceContext $context)
57+
{
58+
$spanHex = str_pad(dechex($context->spanId()), 16, "0", STR_PAD_LEFT);
59+
$traceOptions = $context->enabled() ? self::OPTION_ENABLED : 0;
60+
return pack("CCH*CH*CC", 0, 0, $context->traceId(), 1, $spanHex, 2, $traceOptions);
61+
}
62+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
/**
3+
* Copyright 2017 OpenCensus Authors
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
namespace OpenCensus\Trace\Propagator;
19+
20+
use OpenCensus\Trace\TraceContext;
21+
22+
/**
23+
* This format using a human readable string encoding to propagate TraceContext.
24+
* The current format of the header is <trace-id>[/<span-id>][;o=<options>].
25+
* The options are a bitmask of options. Currently the only option is the
26+
* least significant bit which signals whether the request was traced or not
27+
* (1 = traced, 0 = not traced).
28+
*/
29+
class CloudTraceFormatter implements FormatterInterface
30+
{
31+
const CONTEXT_HEADER_FORMAT = '/([0-9a-f]{32})(?:\/(\d+))?(?:;o=(\d+))?/';
32+
33+
/**
34+
* Generate a TraceContext object from the Trace Context header
35+
*
36+
* @param string $header
37+
* @return TraceContext
38+
*/
39+
public function deserialize($header)
40+
{
41+
if (preg_match(self::CONTEXT_HEADER_FORMAT, $header, $matches)) {
42+
return new TraceContext(
43+
$matches[1],
44+
array_key_exists(2, $matches) ? $matches[2] : null,
45+
array_key_exists(3, $matches) ? $matches[3] == '1' : null,
46+
true
47+
);
48+
}
49+
return new TraceContext();
50+
}
51+
52+
/**
53+
* Convert a TraceContext to header string
54+
*
55+
* @param TraceContext $context
56+
* @return string
57+
*/
58+
public function serialize(TraceContext $context)
59+
{
60+
$ret = '' . $context->traceId();
61+
if ($context->spanId()) {
62+
$ret .= '/' . $context->spanId();
63+
}
64+
$ret .= ';o=' . ($context->enabled() ? '1' : '0');
65+
return $ret;
66+
}
67+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
/**
3+
* Copyright 2017 OpenCensus Authors
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
namespace OpenCensus\Trace\Propagator;
19+
20+
use OpenCensus\Trace\TraceContext;
21+
22+
/**
23+
* This interface lets us define serialization strategies for TraceContext.
24+
*/
25+
interface FormatterInterface
26+
{
27+
/**
28+
* Generate a TraceContext object from the Trace Context header
29+
*
30+
* @param string $header
31+
* @return TraceContext
32+
*/
33+
public function deserialize($header);
34+
35+
/**
36+
* Convert a TraceContext to header string
37+
*
38+
* @param TraceContext $context
39+
* @return string
40+
*/
41+
public function serialize(TraceContext $context);
42+
}

0 commit comments

Comments
 (0)