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

Commit 183478e

Browse files
authored
Add PropagatorInterface and HttpHeaderPropagator (#3)
* Add PropagationFormatterInterface and HttpHeaderFormatter * Dependency injection for TraceContext propagator * Code style fix * HttpHeaderFormatter tests * Add GrpcMetadataFormatter test (skipped for now) * RequestHandler now requires a PropagationFormatterInterface. The RequestTracer new provides the default value. * Code style for multi-line function declaration. * Remove extra doc line * Rename PropagationFormatterInterface -> PropagatorInterface Also rename Formatter -> Propagator. * Update documentation for the Propagator classes
1 parent d29bc59 commit 183478e

10 files changed

Lines changed: 383 additions & 80 deletions
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
/**
3+
* Copyright 2017 Google Inc. All Rights Reserved.
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 will contain the canonical method for propagating
24+
* TraceContext over grpc. The specification is not finalized yet. The
25+
* current design uses a metadata key `grpc-trace-bin` with a binary
26+
* encoding. Do not use this propagator until it's implemented.
27+
*
28+
* @experimental
29+
*/
30+
class GrpcMetadataPropagator implements PropagatorInterface
31+
{
32+
const METADATA_KEY = 'grpc-trace-bin';
33+
34+
/**
35+
* Generate a TraceContext object from the all the HTTP headers
36+
*
37+
* @param array $metadata
38+
* @return TraceContext
39+
*/
40+
public function parse($metadata)
41+
{
42+
if (array_key_exists(self::METADATA_KEY, $metadata)) {
43+
return self::deserialize($metadata[self::METADATA_KEY]);
44+
}
45+
return new TraceContext();
46+
}
47+
48+
/**
49+
* Generate a TraceContext object from the Trace Context header
50+
*
51+
* @param string $header
52+
* @return TraceContext
53+
*/
54+
public function deserialize($bin)
55+
{
56+
// TODO: implement when spec is finalized
57+
return new TraceContext();
58+
}
59+
60+
/**
61+
* Convert a TraceContext to header string
62+
*
63+
* @param TraceContext $context
64+
* @return string
65+
*/
66+
public function serialize(TraceContext $context)
67+
{
68+
// TODO: implement when spec is finalized
69+
return '';
70+
}
71+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
<?php
2+
/**
3+
* Copyright 2017 Google Inc. All Rights Reserved.
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 uses HTTP headers to propagate TraceContext over HTTP.
24+
* There are two possible headers `X-Cloud-Trace-Context` and `Trace-Context`.
25+
* This class handles both formats.
26+
*
27+
* The current format of the header is <trace-id>[/<span-id>][;o=<options>].
28+
* The options are a bitmask of options. Currently the only option is the
29+
* least significant bit which signals whether the request was traced or not
30+
* (1 = traced, 0 = not traced).
31+
*/
32+
class HttpHeaderPropagator implements PropagatorInterface
33+
{
34+
const HTTP_HEADERS = [
35+
'HTTP_X_CLOUD_TRACE_CONTEXT',
36+
'HTTP_TRACE_CONTEXT'
37+
];
38+
const CONTEXT_HEADER_FORMAT = '/([0-9a-f]{32})(?:\/(\d+))?(?:;o=(\d+))?/';
39+
40+
/**
41+
* Generate a TraceContext object from the all the HTTP headers
42+
*
43+
* @param array $headers
44+
* @return TraceContext
45+
*/
46+
public function parse($headers)
47+
{
48+
foreach (self::HTTP_HEADERS as $header) {
49+
if (array_key_exists($header, $headers)) {
50+
return self::deserialize($headers[$header]);
51+
}
52+
}
53+
return new TraceContext();
54+
}
55+
56+
/**
57+
* Generate a TraceContext object from the Trace Context header
58+
*
59+
* @param string $header
60+
* @return TraceContext
61+
*/
62+
public function deserialize($header)
63+
{
64+
if (preg_match(self::CONTEXT_HEADER_FORMAT, $header, $matches)) {
65+
return new TraceContext(
66+
$matches[1],
67+
array_key_exists(2, $matches) ? $matches[2] : null,
68+
array_key_exists(3, $matches) ? $matches[3] == '1' : null,
69+
true
70+
);
71+
}
72+
return new TraceContext();
73+
}
74+
75+
/**
76+
* Convert a TraceContext to header string
77+
*
78+
* @param TraceContext $context
79+
* @return string
80+
*/
81+
public function serialize(TraceContext $context)
82+
{
83+
$ret = '' . $context->traceId();
84+
if ($context->spanId()) {
85+
$ret .= '/' . $context->spanId();
86+
}
87+
$ret .= ';o=' . ($context->enabled() ? '1' : '0');
88+
return $ret;
89+
}
90+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
/**
3+
* Copyright 2017 Google Inc. All Rights Reserved.
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 separate TraceContext Propagator formats.
24+
*/
25+
interface PropagatorInterface
26+
{
27+
/**
28+
* Generate a TraceContext object from the provided container
29+
*
30+
* @param mixed $container
31+
* @return TraceContext
32+
*/
33+
public function parse($container);
34+
35+
/**
36+
* Generate a TraceContext object from the Trace Context header
37+
*
38+
* @param string $header
39+
* @return TraceContext
40+
*/
41+
public function deserialize($header);
42+
43+
/**
44+
* Convert a TraceContext to header string
45+
*
46+
* @param TraceContext $context
47+
* @return string
48+
*/
49+
public function serialize(TraceContext $context);
50+
}

src/Trace/RequestHandler.php

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
use OpenCensus\Trace\Tracer\ExtensionTracer;
2525
use OpenCensus\Trace\Tracer\NullTracer;
2626
use OpenCensus\Trace\Tracer\TracerInterface;
27+
use OpenCensus\Trace\Propagator\PropagatorInterface;
2728

2829
/**
2930
* This class manages the logic for sampling and reporting a trace within a
@@ -76,20 +77,26 @@ class RequestHandler
7677
*
7778
* @param ReporterInterface $reporter How to report the trace at the end of the request
7879
* @param SamplerInterface $sampler Which sampler to use for sampling requests
80+
* @param PropagatorInterface $propagator TraceContext propagator
7981
* @param array $options [optional] {
8082
* Configuration options. See
8183
* {@see OpenCensus\Trace\TraceSpan::__construct()} for the other available options.
8284
*
8385
* @type array $headers Optional array of headers to use in place of $_SERVER
8486
* }
8587
*/
86-
public function __construct(ReporterInterface $reporter, SamplerInterface $sampler, array $options = [])
87-
{
88+
public function __construct(
89+
ReporterInterface $reporter,
90+
SamplerInterface $sampler,
91+
PropagatorInterface $propagator,
92+
array $options = []
93+
) {
8894
$this->reporter = $reporter;
8995
$headers = array_key_exists('headers', $options)
9096
? $options['headers']
9197
: $_SERVER;
92-
$context = TraceContext::fromHeaders($headers);
98+
99+
$context = $propagator->parse($headers);
93100

94101
// If the context force disables tracing, don't consult the $sampler.
95102
if ($context->enabled() !== false) {
@@ -99,7 +106,9 @@ public function __construct(ReporterInterface $reporter, SamplerInterface $sampl
99106
// If the request was provided with a trace context header, we need to send it back with the response
100107
// including whether the request was sampled or not.
101108
if ($context->fromHeader()) {
102-
$this->persistContextHeader($context);
109+
if (!headers_sent()) {
110+
header('X-Cloud-Trace-Context: ' . $propagator->serialize($context));
111+
}
103112
}
104113

105114
$this->tracer = $context->enabled()
@@ -254,11 +263,4 @@ private function detectKey(array $keys, array $array)
254263
}
255264
return null;
256265
}
257-
258-
private function persistContextHeader($context)
259-
{
260-
if (!headers_sent()) {
261-
header('X-Cloud-Trace-Context: ' . $context);
262-
}
263-
}
264266
}

src/Trace/RequestTracer.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
use OpenCensus\Trace\Sampler\SamplerFactory;
2121
use OpenCensus\Trace\Sampler\SamplerInterface;
2222
use OpenCensus\Trace\Reporter\ReporterInterface;
23+
use OpenCensus\Trace\Propagator\PropagatorInterface;
24+
use OpenCensus\Trace\Propagator\HttpHeaderPropagator;
2325

2426
/**
2527
* This class provides static functions to give you access to the current
@@ -115,6 +117,8 @@ class RequestTracer
115117
*
116118
* @type SamplerInterface|array $sampler Sampler or sampler factory build arguments. See
117119
* {@see OpenCensus\Trace\Sampler\SamplerFactory::build()} for the available options.
120+
* @type PropagatorInterface $propagator TraceContext propagator. **Defaults to**
121+
* a new `HttpHeaderPropagator` instance
118122
* @type array $headers Optional array of headers to use in place of $_SERVER
119123
* }
120124
* @return RequestHandler
@@ -128,7 +132,12 @@ public static function start(ReporterInterface $reporter, array $options = [])
128132
? $samplerOptions
129133
: SamplerFactory::build($samplerOptions);
130134

131-
return self::$instance = new RequestHandler($reporter, $sampler, $options);
135+
$propagator = array_key_exists('propagator', $options)
136+
? $options['propagator']
137+
: new HttpHeaderPropagator();
138+
unset($options['propagator']);
139+
140+
return self::$instance = new RequestHandler($reporter, $sampler, $propagator, $options);
132141
}
133142

134143
/**

src/Trace/TraceContext.php

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,6 @@ class TraceContext
3434
{
3535
use IdGeneratorTrait;
3636

37-
const HTTP_HEADER = 'HTTP_X_CLOUD_TRACE_CONTEXT';
38-
const CONTEXT_HEADER_FORMAT = '/([0-9a-f]{32})(?:\/(\d+))?(?:;o=(\d+))?/';
39-
4037
/**
4138
* @var string The current traceId.
4239
*/
@@ -52,26 +49,6 @@ class TraceContext
5249
*/
5350
private $enabled;
5451

55-
/**
56-
* Parses a headers array (normally the $_SERVER variable) and builds a TraceContext objects
57-
*
58-
* @param array $headers The headers array (normally the $_SERVER variable)
59-
* @return TraceContext
60-
*/
61-
public static function fromHeaders($headers)
62-
{
63-
if (array_key_exists(self::HTTP_HEADER, $headers) &&
64-
preg_match(self::CONTEXT_HEADER_FORMAT, $headers[self::HTTP_HEADER], $matches)) {
65-
return new static(
66-
$matches[1],
67-
array_key_exists(2, $matches) ? $matches[2] : null,
68-
array_key_exists(3, $matches) ? $matches[3] == '1' : null,
69-
true
70-
);
71-
}
72-
return new static();
73-
}
74-
7552
/**
7653
* Creates a new TraceContext instance
7754
*
@@ -147,20 +124,4 @@ public function fromHeader()
147124
{
148125
return $this->fromHeader;
149126
}
150-
151-
/**
152-
* Returns a string form of the TraceContext. This is the format of the Trace Context Header
153-
* and should be forwarded to downstream requests as the X-Cloud-Trace-Context header.
154-
*
155-
* @return string
156-
*/
157-
public function __toString()
158-
{
159-
$ret = '' . $this->traceId;
160-
if ($this->spanId) {
161-
$ret .= '/' . $this->spanId;
162-
}
163-
$ret .= ';o=' . ($this->enabled ? '1' : '0');
164-
return $ret;
165-
}
166127
}

0 commit comments

Comments
 (0)