diff --git a/src/Tracing/GuzzleTracingMiddleware.php b/src/Tracing/GuzzleTracingMiddleware.php index fe34dda90..745af61b5 100644 --- a/src/Tracing/GuzzleTracingMiddleware.php +++ b/src/Tracing/GuzzleTracingMiddleware.php @@ -81,12 +81,12 @@ public static function trace(?HubInterface $hub = null): \Closure } if (self::shouldAttachTracingHeaders($sdkOptions, $request)) { - $traceParent = getTraceparent(); + $traceParent = getTraceparent($hub); if ($traceParent !== '') { $request = $request->withHeader('sentry-trace', $traceParent); } - $baggage = getBaggage(); + $baggage = getBaggage($hub); if ($baggage !== '') { $request = $request->withHeader('baggage', $baggage); } diff --git a/src/Tracing/PropagationContext.php b/src/Tracing/PropagationContext.php index 1162484e1..bc1a06a99 100644 --- a/src/Tracing/PropagationContext.php +++ b/src/Tracing/PropagationContext.php @@ -5,6 +5,7 @@ namespace Sentry\Tracing; use Sentry\SentrySdk; +use Sentry\State\HubInterface; use Sentry\State\Scope; use Sentry\Tracing\Traits\TraceHeaderParserTrait; @@ -91,10 +92,10 @@ public function toW3CTraceparent(): string /** * Returns a string that can be used for the `baggage` header & meta tag. */ - public function toBaggage(): string + public function toBaggage(?HubInterface $hub = null): string { if ($this->dynamicSamplingContext === null) { - $hub = SentrySdk::getCurrentHub(); + $hub = $hub ?? SentrySdk::getCurrentHub(); $client = $hub->getClient(); if ($client !== null) { diff --git a/src/functions.php b/src/functions.php index 6e2eb1418..b8c5ecbd7 100644 --- a/src/functions.php +++ b/src/functions.php @@ -383,16 +383,16 @@ function getOtlpTracesEndpointUrl(): ?string * This function is context aware, as in it either returns the traceparent based * on the current span, or the scope's propagation context. */ -function getTraceparent(): string +function getTraceparent(?HubInterface $hub = null): string { - $hub = SentrySdk::getCurrentHub(); + $hub = $hub ?? SentrySdk::getCurrentHub(); $client = $hub->getClient(); if ($client !== null) { $options = $client->getOptions(); if ($options->isTracingEnabled()) { - $span = SentrySdk::getCurrentHub()->getSpan(); + $span = $hub->getSpan(); if ($span !== null) { return $span->toTraceparent(); } @@ -430,16 +430,16 @@ function getW3CTraceparent(): string * This function is context aware, as in it either returns the baggage based * on the current span or the scope's propagation context. */ -function getBaggage(): string +function getBaggage(?HubInterface $hub = null): string { - $hub = SentrySdk::getCurrentHub(); + $hub = $hub ?? SentrySdk::getCurrentHub(); $client = $hub->getClient(); if ($client !== null) { $options = $client->getOptions(); if ($options->isTracingEnabled()) { - $span = SentrySdk::getCurrentHub()->getSpan(); + $span = $hub->getSpan(); if ($span !== null) { return $span->toBaggage(); } @@ -447,12 +447,12 @@ function getBaggage(): string } $baggage = ''; - $hub->configureScope(static function (Scope $scope) use (&$baggage) { + $hub->configureScope(static function (Scope $scope) use (&$baggage, $hub) { if ($scope->hasExternalPropagationContext()) { return; } - $baggage = $scope->getPropagationContext()->toBaggage(); + $baggage = $scope->getPropagationContext()->toBaggage($hub); }); return $baggage; diff --git a/tests/FunctionsTest.php b/tests/FunctionsTest.php index d5f9e64b4..6baae08ba 100644 --- a/tests/FunctionsTest.php +++ b/tests/FunctionsTest.php @@ -683,6 +683,45 @@ public function testBaggageWithTracingEnabled(): void $this->assertSame('sentry-trace_id=566e3688a61d4bc888951642d6f14a19,sentry-sample_rate=1,sentry-transaction=Test,sentry-release=1.0.0,sentry-environment=development,sentry-sampled=true,sentry-sample_rand=0.25', $baggage); } + public function testTraceHeadersUseTheGivenHub(): void + { + $currentHubClient = $this->createMock(ClientInterface::class); + $currentHubClient->method('getOptions')->willReturn(new Options(['traces_sample_rate' => 1.0])); + + $currentHub = new Hub($currentHubClient); + $currentHub->setSpan($currentHub->startTransaction(new TransactionContext())); + + SentrySdk::setCurrentHub($currentHub); + + $client = $this->createMock(ClientInterface::class); + $client->method('getOptions')->willReturn(new Options(['traces_sample_rate' => 1.0])); + + $hub = new Hub($client); + $transaction = $hub->startTransaction(new TransactionContext()); + $hub->setSpan($transaction); + + $this->assertSame($transaction->toTraceparent(), getTraceparent($hub)); + $this->assertSame($transaction->toBaggage(), getBaggage($hub)); + } + + public function testBaggageUsesTheGivenHubWithoutASpan(): void + { + $currentHubClient = $this->createMock(ClientInterface::class); + $currentHubClient->method('getOptions')->willReturn(new Options(['release' => 'current-hub-release'])); + + SentrySdk::setCurrentHub(new Hub($currentHubClient)); + + $client = $this->createMock(ClientInterface::class); + $client->method('getOptions')->willReturn(new Options(['release' => 'given-hub-release'])); + + $hub = new Hub($client); + + $baggage = getBaggage($hub); + + $this->assertStringContainsString('sentry-release=given-hub-release', $baggage); + $this->assertStringNotContainsString('current-hub-release', $baggage); + } + public function testGetOtlpTracesEndpointUrlFallsBackToDsn(): void { $client = $this->createMock(ClientInterface::class); diff --git a/tests/Tracing/GuzzleTracingMiddlewareTest.php b/tests/Tracing/GuzzleTracingMiddlewareTest.php index 86c37f10f..0d0322b8d 100644 --- a/tests/Tracing/GuzzleTracingMiddlewareTest.php +++ b/tests/Tracing/GuzzleTracingMiddlewareTest.php @@ -580,6 +580,36 @@ public function testTraceConcurrentRequests(): void $this->assertSame($transaction, $hub->getSpan()); } + public function testTraceHeadersUseTheSpanOfTheGivenHub(): void + { + $currentHubClient = $this->createMock(ClientInterface::class); + $currentHubClient->method('getOptions')->willReturn(new Options(['traces_sample_rate' => 1])); + $currentHub = new Hub($currentHubClient); + SentrySdk::setCurrentHub($currentHub); + $currentHub->setSpan($currentHub->startTransaction(new TransactionContext())); + + $client = $this->createMock(ClientInterface::class); + $client->method('getOptions')->willReturn(new Options(['traces_sample_rate' => 1])); + $hub = new Hub($client); + $transaction = $hub->startTransaction(new TransactionContext()); + $hub->setSpan($transaction); + + $sentRequests = []; + $function = (GuzzleTracingMiddleware::trace($hub))(static function (Request $request) use ($hub, &$sentRequests): PromiseInterface { + $sentRequests[] = [$request, $hub->getSpan()]; + + return new FulfilledPromise(new Response()); + }); + + $function(new Request('GET', 'https://www.example.com'), []); + + [$request, $span] = $sentRequests[0]; + + $this->assertNotSame($transaction, $span); + $this->assertSame($span->toTraceparent(), $request->getHeaderLine('sentry-trace')); + $this->assertSame($span->toBaggage(), $request->getHeaderLine('baggage')); + } + public function testTraceRestoresTheCurrentSpanWhenTheHandlerThrows(): void { $client = $this->createMock(ClientInterface::class);