Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
3 changes: 3 additions & 0 deletions src/Tracing/GuzzleTracingMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

use function Sentry\getBaggage;
use function Sentry\getTraceparent;
use function Sentry\getW3CTraceparent;

/**
* This handler traces each outgoing HTTP request by recording performance data.
Expand All @@ -33,6 +34,7 @@ public static function trace(?HubInterface $hub = null): \Closure
if (self::shouldAttachTracingHeaders($client, $request)) {
$request = $request
->withHeader('sentry-trace', getTraceparent())
->withHeader('traceparent', getW3CTraceparent())
->withHeader('baggage', getBaggage());
}

Expand Down Expand Up @@ -60,6 +62,7 @@ public static function trace(?HubInterface $hub = null): \Closure
if (self::shouldAttachTracingHeaders($client, $request)) {
$request = $request
->withHeader('sentry-trace', $childSpan->toTraceparent())
->withHeader('traceparent', $childSpan->toW3CTraceparent())
->withHeader('baggage', $childSpan->toBaggage());
}

Expand Down
30 changes: 25 additions & 5 deletions src/Tracing/PropagationContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@

final class PropagationContext
{
private const TRACEPARENT_HEADER_REGEX = '/^[ \\t]*(?<trace_id>[0-9a-f]{32})?-?(?<span_id>[0-9a-f]{16})?-?(?<sampled>[01])?[ \\t]*$/i';
private const SENTRY_TRACEPARENT_HEADER_REGEX = '/^[ \\t]*(?<trace_id>[0-9a-f]{32})?-?(?<span_id>[0-9a-f]{16})?-?(?<sampled>[01])?[ \\t]*$/i';

private const W3C_TRACEPARENT_HEADER_REGEX = '/^[ \\t]*(?<version>[0]{2})?-?(?<trace_id>[0-9a-f]{32})?-?(?<span_id>[0-9a-f]{16})?-?(?<sampled>[01]{2})?[ \\t]*$/i';

/**
* @var TraceId The trace id
Expand Down Expand Up @@ -49,12 +51,12 @@ public static function fromDefaults(): self

public static function fromHeaders(string $sentryTraceHeader, string $baggageHeader): self
{
return self::parseTraceAndBaggage($sentryTraceHeader, $baggageHeader);
return self::parseTraceparentAndBaggage($sentryTraceHeader, $baggageHeader);
}

public static function fromEnvironment(string $sentryTrace, string $baggage): self
{
return self::parseTraceAndBaggage($sentryTrace, $baggage);
return self::parseTraceparentAndBaggage($sentryTrace, $baggage);
}

/**
Expand All @@ -65,6 +67,14 @@ public function toTraceparent(): string
return sprintf('%s-%s', (string) $this->traceId, (string) $this->spanId);
}

/**
* Returns a string that can be used for the W3C `traceparent` header & meta tag.
*/
public function toW3CTraceparent(): string
{
return sprintf('00-%s-%s', (string) $this->traceId, (string) $this->spanId);
}

/**
* Returns a string that can be used for the `baggage` header & meta tag.
*/
Expand Down Expand Up @@ -149,12 +159,22 @@ public function setDynamicSamplingContext(DynamicSamplingContext $dynamicSamplin
return $this;
}

private static function parseTraceAndBaggage(string $sentryTrace, string $baggage): self
private static function parseTraceparentAndBaggage(string $traceparent, string $baggage): self
{
$context = self::fromDefaults();
$hasSentryTrace = false;

if (preg_match(self::TRACEPARENT_HEADER_REGEX, $sentryTrace, $matches)) {
if (preg_match(self::SENTRY_TRACEPARENT_HEADER_REGEX, $traceparent, $matches)) {
if (!empty($matches['trace_id'])) {
$context->traceId = new TraceId($matches['trace_id']);
$hasSentryTrace = true;
}

if (!empty($matches['span_id'])) {
$context->parentSpanId = new SpanId($matches['span_id']);
$hasSentryTrace = true;
}
} elseif (preg_match(self::W3C_TRACEPARENT_HEADER_REGEX, $traceparent, $matches)) {
if (!empty($matches['trace_id'])) {
$context->traceId = new TraceId($matches['trace_id']);
$hasSentryTrace = true;
Expand Down
14 changes: 14 additions & 0 deletions src/Tracing/Span.php
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,20 @@ public function toTraceparent(): string
return sprintf('%s-%s%s', (string) $this->traceId, (string) $this->spanId, $sampled);
}

/**
* Returns a string that can be used for the W3C `traceparent` header & meta tag.
*/
public function toW3CTraceparent(): string
{
$sampled = '';

if ($this->sampled !== null) {
$sampled = $this->sampled ? '-01' : '-00';
}

return sprintf('00-%s-%s%s', (string) $this->traceId, (string) $this->spanId, $sampled);
Copy link
Member Author

Choose a reason for hiding this comment

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

}

/**
* Returns a string that can be used for the `baggage` header & meta tag.
*/
Expand Down
21 changes: 19 additions & 2 deletions src/Tracing/TransactionContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@

final class TransactionContext extends SpanContext
{
private const TRACEPARENT_HEADER_REGEX = '/^[ \\t]*(?<trace_id>[0-9a-f]{32})?-?(?<span_id>[0-9a-f]{16})?-?(?<sampled>[01])?[ \\t]*$/i';
private const SENTRY_TRACEPARENT_HEADER_REGEX = '/^[ \\t]*(?<trace_id>[0-9a-f]{32})?-?(?<span_id>[0-9a-f]{16})?-?(?<sampled>[01])?[ \\t]*$/i';

private const W3C_TRACEPARENT_HEADER_REGEX = '/^[ \\t]*(?<version>[0]{2})?-?(?<trace_id>[0-9a-f]{32})?-?(?<span_id>[0-9a-f]{16})?-?(?<sampled>[01]{2})?[ \\t]*$/i';

public const DEFAULT_NAME = '<unlabeled transaction>';

Expand Down Expand Up @@ -149,7 +151,7 @@ private static function parseTraceAndBaggage(string $sentryTrace, string $baggag
$context = new self();
$hasSentryTrace = false;

if (preg_match(self::TRACEPARENT_HEADER_REGEX, $sentryTrace, $matches)) {
if (preg_match(self::SENTRY_TRACEPARENT_HEADER_REGEX, $sentryTrace, $matches)) {
if (!empty($matches['trace_id'])) {
$context->traceId = new TraceId($matches['trace_id']);
$hasSentryTrace = true;
Expand All @@ -164,6 +166,21 @@ private static function parseTraceAndBaggage(string $sentryTrace, string $baggag
$context->parentSampled = $matches['sampled'] === '1';
$hasSentryTrace = true;
}
} elseif (preg_match(self::W3C_TRACEPARENT_HEADER_REGEX, $sentryTrace, $matches)) {
if (!empty($matches['trace_id'])) {
$context->traceId = new TraceId($matches['trace_id']);
$hasSentryTrace = true;
}

if (!empty($matches['span_id'])) {
$context->parentSpanId = new SpanId($matches['span_id']);
$hasSentryTrace = true;
}

if (isset($matches['sampled'])) {
$context->parentSampled = $matches['sampled'] === '01';
Copy link
Member Author

Choose a reason for hiding this comment

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

https://www.w3.org/TR/trace-context/#trace-flags

While this is a hex-encoded bit field, I think we can get away with just checking for 01, as the other values are either 00 or omitting it altogether.

$hasSentryTrace = true;
}
}

$samplingContext = DynamicSamplingContext::fromHeader($baggage);
Expand Down
32 changes: 31 additions & 1 deletion src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ function trace(callable $trace, SpanContext $context)
}

/**
* Creates the current traceparent string, to be used as a HTTP header value
* Creates the current Sentry traceparent string, to be used as a HTTP header value
* or HTML meta tag value.
* This function is context aware, as in it either returns the traceparent based
* on the current span, or the scope's propagation context.
Expand Down Expand Up @@ -253,6 +253,36 @@ function getTraceparent(): string
return $traceParent;
}

/**
* Creates the current W3C traceparent string, to be used as a HTTP header value
* or HTML meta tag value.
* This function is context aware, as in it either returns the traceparent based
* on the current span, or the scope's propagation context.
*/
function getW3CTraceparent(): string
{
$hub = SentrySdk::getCurrentHub();
$client = $hub->getClient();

if ($client !== null) {
$options = $client->getOptions();

if ($options !== null && $options->isTracingEnabled()) {
$span = SentrySdk::getCurrentHub()->getSpan();
if ($span !== null) {
return $span->toW3CTraceparent();
}
}
}

$traceParent = '';
$hub->configureScope(function (Scope $scope) use (&$traceParent) {
$traceParent = $scope->getPropagationContext()->toW3CTraceparent();
});

return $traceParent;
}

/**
* Creates the baggage content string, to be used as a HTTP header value
* or HTML meta tag value.
Expand Down
44 changes: 44 additions & 0 deletions tests/FunctionsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
use function Sentry\continueTrace;
use function Sentry\getBaggage;
use function Sentry\getTraceparent;
use function Sentry\getW3CTraceparent;
use function Sentry\init;
use function Sentry\startTransaction;
use function Sentry\trace;
Expand Down Expand Up @@ -429,6 +430,49 @@ public function testTraceparentWithTracingEnabled(): void
$this->assertSame('566e3688a61d4bc888951642d6f14a19-566e3688a61d4bc8', $traceParent);
}

public function testW3CTraceparentWithTracingDisabled(): void
{
$propagationContext = PropagationContext::fromDefaults();
$propagationContext->setTraceId(new TraceId('566e3688a61d4bc888951642d6f14a19'));
$propagationContext->setSpanId(new SpanId('566e3688a61d4bc8'));

$scope = new Scope($propagationContext);

$hub = new Hub(null, $scope);

SentrySdk::setCurrentHub($hub);

$traceParent = getW3CTraceparent();

$this->assertSame('00-566e3688a61d4bc888951642d6f14a19-566e3688a61d4bc8', $traceParent);
}

public function testW3CTraceparentWithTracingEnabled(): void
{
$client = $this->createMock(ClientInterface::class);
$client->expects($this->once())
->method('getOptions')
->willReturn(new Options([
'traces_sample_rate' => 1.0,
]));

$hub = new Hub($client);

SentrySdk::setCurrentHub($hub);

$spanContext = (new SpanContext())
->setTraceId(new TraceId('566e3688a61d4bc888951642d6f14a19'))
->setSpanId(new SpanId('566e3688a61d4bc8'));

$span = new Span($spanContext);

$hub->setSpan($span);

$traceParent = getW3CTraceparent();

$this->assertSame('00-566e3688a61d4bc888951642d6f14a19-566e3688a61d4bc8', $traceParent);
}

public function testBaggageWithTracingDisabled(): void
{
$propagationContext = PropagationContext::fromDefaults();
Expand Down
5 changes: 5 additions & 0 deletions tests/Tracing/GuzzleTracingMiddlewareTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,11 @@ public function testTraceHeaders(Request $request, Options $options, bool $heade
$function = $middleware(function (Request $request) use ($expectedPromiseResult, $headersShouldBePresent): PromiseInterface {
if ($headersShouldBePresent) {
$this->assertNotEmpty($request->getHeader('sentry-trace'));
$this->assertNotEmpty($request->getHeader('traceparent'));
$this->assertNotEmpty($request->getHeader('baggage'));
} else {
$this->assertEmpty($request->getHeader('sentry-trace'));
$this->assertEmpty($request->getHeader('traceparent'));
$this->assertEmpty($request->getHeader('baggage'));
}

Expand Down Expand Up @@ -112,9 +114,11 @@ public function testTraceHeadersWithTransacttion(Request $request, Options $opti
$function = $middleware(function (Request $request) use ($expectedPromiseResult, $headersShouldBePresent): PromiseInterface {
if ($headersShouldBePresent) {
$this->assertNotEmpty($request->getHeader('sentry-trace'));
$this->assertNotEmpty($request->getHeader('traceparent'));
$this->assertNotEmpty($request->getHeader('baggage'));
} else {
$this->assertEmpty($request->getHeader('sentry-trace'));
$this->assertEmpty($request->getHeader('traceparent'));
$this->assertEmpty($request->getHeader('baggage'));
}

Expand Down Expand Up @@ -241,6 +245,7 @@ public function testTrace(Request $request, $expectedPromiseResult, array $expec
$middleware = GuzzleTracingMiddleware::trace($hub);
$function = $middleware(function (Request $request) use ($expectedPromiseResult): PromiseInterface {
$this->assertNotEmpty($request->getHeader('sentry-trace'));
$this->assertNotEmpty($request->getHeader('traceparent'));
$this->assertNotEmpty($request->getHeader('baggage'));
if ($expectedPromiseResult instanceof \Throwable) {
return new RejectedPromise($expectedPromiseResult);
Expand Down
17 changes: 17 additions & 0 deletions tests/Tracing/PropagationContextTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@ public static function tracingDataProvider(): iterable
true,
];

yield [
'00-566e3688a61d4bc888951642d6f14a19-566e3688a61d4bc8-01',
'',
new TraceId('566e3688a61d4bc888951642d6f14a19'),
new SpanId('566e3688a61d4bc8'),
true,
];

yield [
'566e3688a61d4bc888951642d6f14a19-566e3688a61d4bc8-1',
'sentry-public_key=public,sentry-trace_id=566e3688a61d4bc888951642d6f14a19,sentry-sample_rate=1',
Expand All @@ -104,6 +112,15 @@ public function testToTraceparent()
$this->assertSame('566e3688a61d4bc888951642d6f14a19-566e3688a61d4bc8', $propagationContext->toTraceparent());
}

public function testToW3CTraceparent()
{
$propagationContext = PropagationContext::fromDefaults();
$propagationContext->setTraceId(new TraceId('566e3688a61d4bc888951642d6f14a19'));
$propagationContext->setSpanId(new SpanId('566e3688a61d4bc8'));

$this->assertSame('00-566e3688a61d4bc888951642d6f14a19-566e3688a61d4bc8', $propagationContext->toW3CTraceparent());
}

public function testToBaggage()
{
$dynamicSamplingContext = DynamicSamplingContext::fromHeader('sentry-trace_id=566e3688a61d4bc888951642d6f14a19');
Expand Down
30 changes: 30 additions & 0 deletions tests/Tracing/TransactionContextTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,36 @@ public static function tracingDataProvider(): iterable
true,
];

yield [
'00-566e3688a61d4bc888951642d6f14a19-566e3688a61d4bc8-00',
'',
new SpanId('566e3688a61d4bc8'),
new TraceId('566e3688a61d4bc888951642d6f14a19'),
false,
DynamicSamplingContext::class,
true,
];

yield [
'00-566e3688a61d4bc888951642d6f14a19-566e3688a61d4bc8-01',
'',
new SpanId('566e3688a61d4bc8'),
new TraceId('566e3688a61d4bc888951642d6f14a19'),
true,
DynamicSamplingContext::class,
true,
];

yield [
'00-566e3688a61d4bc888951642d6f14a19-566e3688a61d4bc8',
'',
new SpanId('566e3688a61d4bc8'),
new TraceId('566e3688a61d4bc888951642d6f14a19'),
null,
DynamicSamplingContext::class,
true,
];

yield [
'566e3688a61d4bc888951642d6f14a19-566e3688a61d4bc8-1',
'sentry-public_key=public,sentry-trace_id=566e3688a61d4bc888951642d6f14a19,sentry-sample_rate=1',
Expand Down