Skip to content

Commit

Permalink
Add support for HTTP Client baggage propagation (#675)
Browse files Browse the repository at this point in the history
Co-authored-by: Michi Hoffmann <cleptric@users.noreply.github.com>
  • Loading branch information
DAcodedBEAT and cleptric committed Nov 24, 2022
1 parent b1b8b17 commit 38f70b7
Show file tree
Hide file tree
Showing 10 changed files with 125 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -5,6 +5,7 @@
- feat: Add support for tracing of the Symfony HTTP client requests (#606)
- feat: Support logging the impersonator user, if any (#647)
- ref: Use constant for the SDK version (#662)
- Add support for HTTP client baggage propagation (#663)

## 4.4.0 (2022-10-20)

Expand Down
5 changes: 5 additions & 0 deletions src/DependencyInjection/Configuration.php
Expand Up @@ -48,6 +48,7 @@ public function getConfigTreeBuilder(): TreeBuilder
->arrayNode('options')
->addDefaultsIfNotSet()
->fixXmlConfig('integration')
->fixXmlConfig('trace_propagation_target')
->fixXmlConfig('tag')
->fixXmlConfig('class_serializer')
->fixXmlConfig('prefix', 'prefixes')
Expand All @@ -72,6 +73,10 @@ public function getConfigTreeBuilder(): TreeBuilder
->info('The sampling factor to apply to transactions. A value of 0 will deny sending any transaction, and a value of 1 will send all transactions.')
->end()
->scalarNode('traces_sampler')->end()
->arrayNode('trace_propagation_targets')
->scalarPrototype()->end()
->beforeNormalization()->castToArray()->end()
->end()
->booleanNode('attach_stacktrace')->end()
->integerNode('context_lines')->min(0)->end()
->booleanNode('enable_compression')->end()
Expand Down
1 change: 1 addition & 0 deletions src/Resources/config/schema/sentry-1.0.xsd
Expand Up @@ -23,6 +23,7 @@
<xsd:complexType name="options">
<xsd:sequence>
<xsd:element name="integration" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="trace-propagation-target" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="prefix" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="tag" type="tag" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="in-app-exclude" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
Expand Down
13 changes: 13 additions & 0 deletions src/Tracing/HttpClient/AbstractTraceableHttpClient.php
Expand Up @@ -4,6 +4,7 @@

namespace Sentry\SentryBundle\Tracing\HttpClient;

use GuzzleHttp\Psr7\Uri;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerInterface;
use Sentry\State\HubInterface;
Expand Down Expand Up @@ -49,6 +50,18 @@ public function request(string $method, string $url, array $options = []): Respo
if (null !== $parent) {
$headers = $options['headers'] ?? [];
$headers['sentry-trace'] = $parent->toTraceparent();

// Check if the request destination is allow listed in the trace_propagation_targets option.
$client = $this->hub->getClient();
if (null !== $client) {
$sdkOptions = $client->getOptions();
$uri = new Uri($url);

if (\in_array($uri->getHost(), $sdkOptions->getTracePropagationTargets())) {
$headers['baggage'] = $parent->toBaggage();
}
}

$options['headers'] = $headers;

$context = new SpanContext();
Expand Down
1 change: 1 addition & 0 deletions tests/DependencyInjection/ConfigurationTest.php
Expand Up @@ -26,6 +26,7 @@ public function testProcessConfigurationWithDefaultConfiguration(): void
'options' => [
'integrations' => [],
'prefixes' => array_merge(['%kernel.project_dir%'], array_filter(explode(\PATH_SEPARATOR, get_include_path() ?: ''))),
'trace_propagation_targets' => [],
'environment' => '%kernel.environment%',
'release' => PrettyVersions::getRootPackageVersion()->getPrettyVersion(),
'tags' => [],
Expand Down
1 change: 1 addition & 0 deletions tests/DependencyInjection/Fixtures/php/full.php
Expand Up @@ -17,6 +17,7 @@
'sample_rate' => 1,
'traces_sample_rate' => 1,
'traces_sampler' => 'App\\Sentry\\Tracing\\TracesSampler',
'trace_propagation_targets' => ['website.invalid'],
'attach_stacktrace' => true,
'context_lines' => 0,
'enable_compression' => true,
Expand Down
1 change: 1 addition & 0 deletions tests/DependencyInjection/Fixtures/xml/full.xml
Expand Up @@ -36,6 +36,7 @@
max-request-body-size="none"
>
<sentry:integration>App\Sentry\Integration\FooIntegration</sentry:integration>
<sentry:trace-propagation-target>website.invalid</sentry:trace-propagation-target>
<sentry:prefix>%kernel.project_dir%</sentry:prefix>
<sentry:tag name="context">development</sentry:tag>
<sentry:in-app-exclude>%kernel.cache_dir%</sentry:in-app-exclude>
Expand Down
2 changes: 2 additions & 0 deletions tests/DependencyInjection/Fixtures/yml/full.yml
Expand Up @@ -12,6 +12,8 @@ sentry:
sample_rate: 1
traces_sample_rate: 1
traces_sampler: App\Sentry\Tracing\TracesSampler
trace_propagation_targets:
- 'website.invalid'
attach_stacktrace: true
context_lines: 0
enable_compression: true
Expand Down
1 change: 1 addition & 0 deletions tests/DependencyInjection/SentryExtensionTest.php
Expand Up @@ -202,6 +202,7 @@ public function testClientIsCreatedFromOptions(): void
'sample_rate' => 1,
'traces_sample_rate' => 1,
'traces_sampler' => new Reference('App\\Sentry\\Tracing\\TracesSampler'),
'trace_propagation_targets' => ['website.invalid'],
'attach_stacktrace' => true,
'context_lines' => 0,
'enable_compression' => true,
Expand Down
99 changes: 99 additions & 0 deletions tests/Tracing/HttpClient/TraceableHttpClientTest.php
Expand Up @@ -8,6 +8,8 @@
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\NullLogger;
use Sentry\ClientInterface;
use Sentry\Options;
use Sentry\SentryBundle\Tracing\HttpClient\AbstractTraceableResponse;
use Sentry\SentryBundle\Tracing\HttpClient\TraceableHttpClient;
use Sentry\State\HubInterface;
Expand Down Expand Up @@ -68,6 +70,7 @@ public function testRequest(): void
$this->assertSame('GET', $response->getInfo('http_method'));
$this->assertSame('https://www.example.com/test-page', $response->getInfo('url'));
$this->assertSame(['sentry-trace: ' . $transaction->toTraceparent()], $mockResponse->getRequestOptions()['normalized_headers']['sentry-trace']);
$this->assertArrayNotHasKey('baggage', $mockResponse->getRequestOptions()['normalized_headers']);
$this->assertNotNull($transaction->getSpanRecorder());

$spans = $transaction->getSpanRecorder()->getSpans();
Expand All @@ -83,6 +86,102 @@ public function testRequest(): void
$this->assertSame($expectedTags, $spans[1]->getTags());
}

public function testRequestDoesNotContainBaggageHeader(): void
{
$options = new Options([
'dsn' => 'http://public:secret@example.com/sentry/1',
'trace_propagation_targets' => ['non-matching-host.invalid'],
]);
$client = $this->createMock(ClientInterface::class);
$client
->expects($this->once())
->method('getOptions')
->willReturn($options);

$transaction = new Transaction(new TransactionContext());
$transaction->initSpanRecorder();

$this->hub->expects($this->once())
->method('getSpan')
->willReturn($transaction);
$this->hub->expects($this->once())
->method('getClient')
->willReturn($client);

$mockResponse = new MockResponse();
$decoratedHttpClient = new MockHttpClient($mockResponse);
$httpClient = new TraceableHttpClient($decoratedHttpClient, $this->hub);
$response = $httpClient->request('PUT', 'https://www.example.com/test-page');

$this->assertInstanceOf(AbstractTraceableResponse::class, $response);
$this->assertSame(200, $response->getStatusCode());
$this->assertSame('PUT', $response->getInfo('http_method'));
$this->assertSame('https://www.example.com/test-page', $response->getInfo('url'));
$this->assertSame(['sentry-trace: ' . $transaction->toTraceparent()], $mockResponse->getRequestOptions()['normalized_headers']['sentry-trace']);
$this->assertArrayNotHasKey('baggage', $mockResponse->getRequestOptions()['normalized_headers']);
$this->assertNotNull($transaction->getSpanRecorder());

$spans = $transaction->getSpanRecorder()->getSpans();
$expectedTags = [
'http.method' => 'PUT',
'http.url' => 'https://www.example.com/test-page',
];

$this->assertCount(2, $spans);
$this->assertNull($spans[1]->getEndTimestamp());
$this->assertSame('http.client', $spans[1]->getOp());
$this->assertSame('HTTP PUT', $spans[1]->getDescription());
$this->assertSame($expectedTags, $spans[1]->getTags());
}

public function testRequestDoesContainBaggageHeader(): void
{
$options = new Options([
'dsn' => 'http://public:secret@example.com/sentry/1',
'trace_propagation_targets' => ['www.example.com'],
]);
$client = $this->createMock(ClientInterface::class);
$client
->expects($this->once())
->method('getOptions')
->willReturn($options);

$transaction = new Transaction(new TransactionContext());
$transaction->initSpanRecorder();

$this->hub->expects($this->once())
->method('getSpan')
->willReturn($transaction);
$this->hub->expects($this->once())
->method('getClient')
->willReturn($client);

$mockResponse = new MockResponse();
$decoratedHttpClient = new MockHttpClient($mockResponse);
$httpClient = new TraceableHttpClient($decoratedHttpClient, $this->hub);
$response = $httpClient->request('POST', 'https://www.example.com/test-page');

$this->assertInstanceOf(AbstractTraceableResponse::class, $response);
$this->assertSame(200, $response->getStatusCode());
$this->assertSame('POST', $response->getInfo('http_method'));
$this->assertSame('https://www.example.com/test-page', $response->getInfo('url'));
$this->assertSame(['sentry-trace: ' . $transaction->toTraceparent()], $mockResponse->getRequestOptions()['normalized_headers']['sentry-trace']);
$this->assertSame(['baggage: ' . $transaction->toBaggage()], $mockResponse->getRequestOptions()['normalized_headers']['baggage']);
$this->assertNotNull($transaction->getSpanRecorder());

$spans = $transaction->getSpanRecorder()->getSpans();
$expectedTags = [
'http.method' => 'POST',
'http.url' => 'https://www.example.com/test-page',
];

$this->assertCount(2, $spans);
$this->assertNull($spans[1]->getEndTimestamp());
$this->assertSame('http.client', $spans[1]->getOp());
$this->assertSame('HTTP POST', $spans[1]->getDescription());
$this->assertSame($expectedTags, $spans[1]->getTags());
}

public function testStream(): void
{
$transaction = new Transaction(new TransactionContext());
Expand Down

0 comments on commit 38f70b7

Please sign in to comment.