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

Add B3 multiple headers propagation #224

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Trace/Integrations/Guzzle/EventSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public function onBefore(BeforeEvent $event)
$request = $event->getRequest();
$context = Tracer::spanContext();
if ($context->enabled()) {
$request->setHeader($this->propagator->key(), $this->propagator->formatter()->serialize($context));
$request->setHeaders($this->propagator->inject($context, []));
}
$span = Tracer::startSpan([
'name' => 'GuzzleHttp::request',
Expand Down
9 changes: 5 additions & 4 deletions src/Trace/Integrations/Guzzle/Middleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,11 @@ public function __invoke(callable $handler)
return function (RequestInterface $request, $options) use ($handler) {
$context = Tracer::spanContext();
if ($context->enabled()) {
$request = $request->withHeader(
$this->propagator->key(),
$this->propagator->formatter()->serialize($context)
);
$headers = $this->propagator->inject($context, []) ;

foreach ($headers as $headerName => $headerValue) {
$request = $request->withHeader($headerName, $headerValue);
}
}
return Tracer::inSpan([
'name' => 'GuzzleHttp::request',
Expand Down
58 changes: 58 additions & 0 deletions src/Trace/Propagator/B3HeadersPropagator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

namespace OpenCensus\Trace\Propagator;

use OpenCensus\Trace\SpanContext;

/**
* @see https://github.com/openzipkin/b3-propagation
*/
class B3HeadersPropagator implements PropagatorInterface
{
private const X_B3_TRACE_ID = 'X-B3-TraceId';
private const X_B3_SPAN_ID = 'X-B3-SpanId';
private const X_B3_SAMPLED = 'X-B3-Sampled';
private const X_B3_FLAGS = 'X-B3-Flags';

/**
* Extract the SpanContext from some container
*
* @param mixed $container
* @return SpanContext
*/
public function extract($container)
{
$traceId = $container[self::X_B3_TRACE_ID] ?? null;
$spanId = $container[self::X_B3_SPAN_ID] ?? null;
$sampled = $container[self::X_B3_SAMPLED] ?? null;
$flags = $container[self::X_B3_FLAGS] ?? null;

$enabled = null;

if ($sampled !== null) {
$enabled = $sampled === '1';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is possible some propagators pass true aswell according to https://github.com/openzipkin/b3-propagation#sampling-state-1

}

if ($flags === '1') {
$enabled = true;
}

return new SpanContext($traceId, $spanId, $enabled, true);
}

/**
* Inject the SpanContext back into the response
*
* @param SpanContext $context
* @param mixed $container
* @return array
*/
public function inject(SpanContext $context, $container)
{
return [
self::X_B3_TRACE_ID => $context->traceId(),
self::X_B3_SPAN_ID => $context->spanId(),
self::X_B3_SAMPLED => $context->enabled() ? 1 : 0,
] + $container;
}
}
2 changes: 1 addition & 1 deletion src/Trace/Propagator/FormatterInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ interface FormatterInterface
/**
* Generate a SpanContext object from the Trace Context header
*
* @param string $header
* @param string[] $header
* @return SpanContext
*/
public function deserialize($header);
Expand Down
20 changes: 0 additions & 20 deletions src/Trace/Propagator/GrpcMetadataPropagator.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,24 +78,4 @@ public function inject(SpanContext $context, $metadata)
$metadata[$this->key] = $this->formatter->serialize($context);
return $metadata;
}

/**
* Fetch the formatter for propagating the SpanContext
*
* @return FormatterInterface
*/
public function formatter()
{
return $this->formatter;
}

/**
* Return the key used to propagate the SpanContext
*
* @return string
*/
public function key()
{
return $this->key;
}
}
32 changes: 5 additions & 27 deletions src/Trace/Propagator/HttpHeaderPropagator.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
*/
class HttpHeaderPropagator implements PropagatorInterface
{
const DEFAULT_HEADER = 'HTTP_X_CLOUD_TRACE_CONTEXT';
const DEFAULT_HEADER = 'X-Cloud-Trace-Context';

/**
* @var FormatterInterface
Expand All @@ -44,7 +44,7 @@ class HttpHeaderPropagator implements PropagatorInterface
* deserialize SpanContext. **Defaults to** a new
* CloudTraceFormatter.
* @param string $key [optional] The header key to store/retrieve the
* encoded SpanContext. **Defaults to** `HTTP_X_CLOUD_TRACE_CONTEXT`
* encoded SpanContext. **Defaults to** `X-Cloud-Trace-Context`
*/
public function __construct(FormatterInterface $formatter = null, $header = null)
{
Expand Down Expand Up @@ -75,33 +75,11 @@ public function extract($headers)
*/
public function inject(SpanContext $context, $container)
{
$header = $this->key();
$header = $this->header;
$value = $this->formatter->serialize($context);
if (!headers_sent()) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i am not sure if i should keep it, it shouldn't cause any side effects IMHO

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like a backwards incompatible change if you remove this code. If someone was relying on having the headers written with this library, we'd remove that functionality. However, since the package is in 0.x version, I suppose this is allowed.

header("$header: $value");
}
return [
$header => $value
];
}

/**
* Returns the current formatter
*
* @return FormatterInterface
*/
public function formatter()
{
return $this->formatter;
}
$container[$header] = $value;

/**
* Return the key used to propagate the SpanContext
*
* @return string
*/
public function key()
{
return str_replace('_', '-', preg_replace('/^HTTP_/', '', $this->header));
return $container;
}
}
14 changes: 0 additions & 14 deletions src/Trace/Propagator/PropagatorInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,4 @@ public function extract($container);
* @return array
*/
public function inject(SpanContext $context, $container);

/**
* Fetch the formatter for propagating the SpanContext
*
* @return FormatterInterface
*/
public function formatter();

/**
* Return the key used to propagate the SpanContext
*
* @return string
*/
public function key();
}
6 changes: 6 additions & 0 deletions src/Trace/SpanContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ class SpanContext
*/
private $enabled;


/**
* @var bool Whether or not this context was detected from a request header.
*/
private $fromHeader;

/**
* Creates a new SpanContext instance
*
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/guzzle5/tests/Guzzle5Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ function (RequestInterface $request, ResponseInterface &$response) {
$tracer = Tracer::start($exporter->reveal(), [
'skipReporting' => true,
'headers' => [
'HTTP_X_CLOUD_TRACE_CONTEXT' => $traceContextHeader
'X-Cloud-Trace-Context' => $traceContextHeader
]
]);

Expand Down
2 changes: 1 addition & 1 deletion tests/integration/guzzle6/tests/Guzzle6Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ function (RequestInterface $request, ResponseInterface &$response) {
$tracer = Tracer::start($exporter->reveal(), [
'skipReporting' => true,
'headers' => [
'HTTP_X_CLOUD_TRACE_CONTEXT' => $traceContextHeader
'X-Cloud-Trace-Context' => $traceContextHeader
]
]);

Expand Down
4 changes: 2 additions & 2 deletions tests/unit/Trace/Integrations/Guzzle/EventSubscriberTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ public function testAddsSpanContextHeader()

$request = $history->getLastRequest();
$headers = $request->getHeaders();
$this->assertArrayHasKey('X-CLOUD-TRACE-CONTEXT', $headers);
$this->assertRegExp('/[0-9a-f]+\/4660;o=1/', $headers['X-CLOUD-TRACE-CONTEXT'][0]);
$this->assertArrayHasKey('X-Cloud-Trace-Context', $headers);
$this->assertRegExp('/[0-9a-f]+\/4660;o=1/', $headers['X-Cloud-Trace-Context'][0]);

$rt->onExit();
}
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/Trace/Integrations/Guzzle/MiddlewareTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public function testAddsSpanContextHeader()
$req = $this->prophesize(RequestInterface::class);
$req->getMethod()->willReturn('GET')->shouldBeCalled();
$req->getUri()->willReturn('/')->shouldBeCalled();
$req->withHeader('X-CLOUD-TRACE-CONTEXT', Argument::that(function ($val) {
$req->withHeader('X-Cloud-Trace-Context', Argument::that(function ($val) {
return preg_match('/[0-9a-f]+\/4660;o=1/', $val);
}))->willReturn($req->reveal())->shouldBeCalled();
$request = $req->reveal();
Expand Down
80 changes: 80 additions & 0 deletions tests/unit/Trace/Propagator/B3HeadersPropagatorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php

namespace OpenCensus\Tests\Unit\Trace\Propagator;

use OpenCensus\Trace\Propagator\B3HeadersPropagator;
use OpenCensus\Trace\Propagator\HttpHeaderPropagator;
use OpenCensus\Trace\SpanContext;
use PHPUnit\Framework\TestCase;

/**
* @group trace
*/
class B3HeadersPropagatorTest extends TestCase
{
/**
* @dataProvider traceMetadata
*/
public function testExtract($traceId, $spanId, $enabled, $headers)
{
$propagator = new B3HeadersPropagator();
$context = $propagator->extract($headers);
$this->assertEquals($traceId, $context->traceId());
$this->assertEquals($spanId, $context->spanId());
$this->assertEquals($enabled, $context->enabled());
$this->assertTrue($context->fromHeader());
}

/**
* @dataProvider traceMetadata
*/
public function testInject($traceId, $spanId, $enabled, $headers)
{
$propagator = new B3HeadersPropagator();
$context = new SpanContext($traceId, $spanId, $enabled);
$output = $propagator->inject($context, []);

if (array_key_exists('X-B3-Flags', $headers)) {
$headers['X-B3-Sampled'] = $headers['X-B3-Flags'];
unset($headers['X-B3-Flags']);
}

$this->assertEquals($headers, $output);
}

public function traceMetadata()
{
return [
[
'463ac35c9f6413ad48485a3953bb6124',
'a2fb4a1d1a96d312',
true,
[
'X-B3-TraceId' => '463ac35c9f6413ad48485a3953bb6124',
'X-B3-SpanId' => 'a2fb4a1d1a96d312',
'X-B3-Sampled' => '1',
],
],
[
'463ac35c9f6413ad48485a3953bb6124',
'a2fb4a1d1a96d312',
false,
[
'X-B3-TraceId' => '463ac35c9f6413ad48485a3953bb6124',
'X-B3-SpanId' => 'a2fb4a1d1a96d312',
'X-B3-Sampled' => '0',
],
],
[
'463ac35c9f6413ad48485a3953bb6124',
'a2fb4a1d1a96d312',
true,
[
'X-B3-TraceId' => '463ac35c9f6413ad48485a3953bb6124',
'X-B3-SpanId' => 'a2fb4a1d1a96d312',
'X-B3-Flags' => '1',
],
],
];
}
}
16 changes: 8 additions & 8 deletions tests/unit/Trace/Propagator/HttpHeaderPropagatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class HttpHeaderPropagatorTest extends TestCase
public function testExtract($traceId, $spanId, $enabled, $header)
{
$propagator = new HttpHeaderPropagator();
$context = $propagator->extract(['HTTP_X_CLOUD_TRACE_CONTEXT' => $header]);
$context = $propagator->extract(['X-Cloud-Trace-Context' => $header]);
$this->assertEquals($traceId, $context->traceId());
$this->assertEquals($spanId, $context->spanId());
$this->assertEquals($enabled, $context->enabled());
Expand All @@ -45,8 +45,8 @@ public function testExtract($traceId, $spanId, $enabled, $header)
*/
public function testExtractCustomKey($traceId, $spanId, $enabled, $header)
{
$propagator = new HttpHeaderPropagator(new CloudTraceFormatter(), 'HTTP_TRACE_CONTEXT');
$context = $propagator->extract(['HTTP_TRACE_CONTEXT' => $header]);
$propagator = new HttpHeaderPropagator(new CloudTraceFormatter(), 'Trace-Context');
$context = $propagator->extract(['Trace-Context' => $header]);
$this->assertEquals($traceId, $context->traceId());
$this->assertEquals($spanId, $context->spanId());
$this->assertEquals($enabled, $context->enabled());
Expand All @@ -61,20 +61,20 @@ public function testInject($traceId, $spanId, $enabled, $header)
$propagator = new HttpHeaderPropagator();
$context = new SpanContext($traceId, $spanId, $enabled);
$output = $propagator->inject($context, []);
$this->assertArrayHasKey('X-CLOUD-TRACE-CONTEXT', $output);
$this->assertEquals($header, $output['X-CLOUD-TRACE-CONTEXT']);
$this->assertArrayHasKey('X-Cloud-Trace-Context', $output);
$this->assertEquals($header, $output['X-Cloud-Trace-Context']);
}

/**
* @dataProvider traceMetadata
*/
public function testInjectCustomKey($traceId, $spanId, $enabled, $header)
{
$propagator = new HttpHeaderPropagator(new CloudTraceFormatter(), 'HTTP_TRACE_CONTEXT');
$propagator = new HttpHeaderPropagator(new CloudTraceFormatter(), 'Trace-Context');
$context = new SpanContext($traceId, $spanId, $enabled);
$output = $propagator->inject($context, []);
$this->assertArrayHasKey('TRACE-CONTEXT', $output);
$this->assertEquals($header, $output['TRACE-CONTEXT']);
$this->assertArrayHasKey('Trace-Context', $output);
$this->assertEquals($header, $output['Trace-Context']);
}

/**
Expand Down
6 changes: 3 additions & 3 deletions tests/unit/Trace/RequestHandlerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public function testCanParseParentContext()
new HttpHeaderPropagator(),
[
'headers' => [
'HTTP_X_CLOUD_TRACE_CONTEXT' => '12345678901234567890123456789012/5555;o=1'
'X-Cloud-Trace-Context' => '12345678901234567890123456789012/5555;o=1'
],
'skipReporting' => true
]
Expand All @@ -106,7 +106,7 @@ public function testForceEnabledContextHeader()
new HttpHeaderPropagator(),
[
'headers' => [
'HTTP_X_CLOUD_TRACE_CONTEXT' => '12345678901234567890123456789012;o=1'
'X-Cloud-Trace-Context' => '12345678901234567890123456789012;o=1'
],
'skipReporting' => true
]
Expand All @@ -124,7 +124,7 @@ public function testForceDisabledContextHeader()
new HttpHeaderPropagator(),
[
'headers' => [
'HTTP_X_CLOUD_TRACE_CONTEXT' => '12345678901234567890123456789012;o=0'
'X-Cloud-Trace-Context' => '12345678901234567890123456789012;o=0'
],
'skipReporting' => true
]
Expand Down