Skip to content

Commit 9a0a8e8

Browse files
authored
Add conditional traceresponse propagation to Symfony auto-instrumentation (#146)
Add composer suggest dont mention x-ray CS Fix: Fix import order
1 parent 120229c commit 9a0a8e8

File tree

5 files changed

+114
-21
lines changed

5 files changed

+114
-21
lines changed

composer.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
"symfony/http-kernel": "*",
1515
"symfony/http-client-contracts": "*"
1616
},
17+
"suggest": {
18+
"open-telemetry/opentelemetry-propagation-traceresponse": "Automatically propagate the context to the client."
19+
},
1720
"require-dev": {
1821
"friendsofphp/php-cs-fixer": "^3",
1922
"phan/phan": "^5.0",
@@ -24,7 +27,8 @@
2427
"open-telemetry/sdk": "^1.0",
2528
"phpunit/phpunit": "^9.5",
2629
"vimeo/psalm": "^4.0",
27-
"symfony/http-client": "^5.4||^6.0"
30+
"symfony/http-client": "^5.4||^6.0",
31+
"open-telemetry/opentelemetry-propagation-traceresponse": "*"
2832
},
2933
"autoload": {
3034
"psr-4": {
@@ -38,5 +42,10 @@
3842
"psr-4": {
3943
"OpenTelemetry\\Tests\\Instrumentation\\Symfony\\": "tests/"
4044
}
45+
},
46+
"config": {
47+
"allow-plugins": {
48+
"php-http/discovery": false
49+
}
4150
}
4251
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
/**
1212
* @internal
1313
*/
14-
final class HeadersPropagator implements PropagationGetterInterface
14+
final class RequestPropagationGetter implements PropagationGetterInterface
1515
{
1616
public static function instance(): self
1717
{

src/ResponsePropagationSetter.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenTelemetry\Contrib\Instrumentation\Symfony;
6+
7+
use function assert;
8+
use OpenTelemetry\Context\Propagation\PropagationSetterInterface;
9+
use Symfony\Component\HttpFoundation\Response;
10+
11+
/**
12+
* @internal
13+
*/
14+
final class ResponsePropagationSetter implements PropagationSetterInterface
15+
{
16+
public static function instance(): self
17+
{
18+
static $instance;
19+
20+
return $instance ??= new self();
21+
}
22+
23+
/** @psalm-suppress InvalidReturnType */
24+
public function keys($carrier): array
25+
{
26+
assert($carrier instanceof Response);
27+
/** @psalm-suppress InvalidReturnStatement */
28+
return $carrier->headers->keys();
29+
}
30+
31+
public function set(&$carrier, string $key, string $value): void
32+
{
33+
assert($carrier instanceof Response);
34+
35+
$carrier->headers->set($key, $value);
36+
}
37+
}

src/SymfonyInstrumentation.php

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public static function register(): void
4949

5050
$parent = Context::getCurrent();
5151
if ($request) {
52-
$parent = Globals::propagator()->extract($request, HeadersPropagator::instance());
52+
$parent = Globals::propagator()->extract($request, RequestPropagationGetter::instance());
5353
$span = $builder
5454
->setParent($parent)
5555
->setAttribute(TraceAttributes::HTTP_URL, $request->getUri())
@@ -94,19 +94,29 @@ public static function register(): void
9494
$span->setStatus(StatusCode::STATUS_ERROR, $exception->getMessage());
9595
}
9696

97-
if ($response) {
98-
if ($response->getStatusCode() >= Response::HTTP_BAD_REQUEST) {
99-
$span->setStatus(StatusCode::STATUS_ERROR);
100-
}
101-
$span->setAttribute(TraceAttributes::HTTP_STATUS_CODE, $response->getStatusCode());
102-
$span->setAttribute(TraceAttributes::HTTP_FLAVOR, $response->getProtocolVersion());
103-
$contentLength = $response->headers->get('Content-Length');
104-
/** @psalm-suppress PossiblyFalseArgument */
105-
if (null === $contentLength && is_string($response->getContent())) {
106-
$contentLength = \strlen($response->getContent());
107-
}
97+
if (null === $response) {
98+
$span->end();
99+
100+
return;
101+
}
102+
103+
if ($response->getStatusCode() >= Response::HTTP_BAD_REQUEST) {
104+
$span->setStatus(StatusCode::STATUS_ERROR);
105+
}
106+
$span->setAttribute(TraceAttributes::HTTP_STATUS_CODE, $response->getStatusCode());
107+
$span->setAttribute(TraceAttributes::HTTP_FLAVOR, $response->getProtocolVersion());
108+
$contentLength = $response->headers->get('Content-Length');
109+
/** @psalm-suppress PossiblyFalseArgument */
110+
if (null === $contentLength && is_string($response->getContent())) {
111+
$contentLength = \strlen($response->getContent());
112+
}
113+
114+
$span->setAttribute(TraceAttributes::HTTP_RESPONSE_CONTENT_LENGTH, $contentLength);
108115

109-
$span->setAttribute(TraceAttributes::HTTP_RESPONSE_CONTENT_LENGTH, $contentLength);
116+
// Propagate traceresponse header to response, if TraceResponsePropagator is present
117+
if (class_exists('OpenTelemetry\Contrib\Propagation\TraceResponse\TraceResponsePropagator')) {
118+
$prop = new \OpenTelemetry\Contrib\Propagation\TraceResponse\TraceResponsePropagator();
119+
$prop->inject($response, ResponsePropagationSetter::instance(), $scope->context());
110120
}
111121

112122
$span->end();

tests/Integration/SymfonyInstrumentationTest.php

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace OpenTelemetry\Tests\Instrumentation\Symfony\tests\Integration;
66

7+
use OpenTelemetry\Contrib\Propagation\TraceResponse\TraceResponsePropagator;
78
use OpenTelemetry\SemConv\TraceAttributes;
89
use Symfony\Component\EventDispatcher\EventDispatcher;
910
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
@@ -27,7 +28,13 @@ public function test_http_kernel_handle_exception(): void
2728
});
2829
$this->assertCount(0, $this->storage);
2930

30-
$kernel->handle(new Request());
31+
$response = $kernel->handle(new Request());
32+
33+
$this->assertArrayHasKey(
34+
TraceResponsePropagator::TRACERESPONSE,
35+
$response->headers->all(),
36+
'traceresponse header is present if TraceResponsePropagator is present'
37+
);
3138
}
3239

3340
public function test_http_kernel_handle_attributes(): void
@@ -37,7 +44,7 @@ public function test_http_kernel_handle_attributes(): void
3744
$request = new Request();
3845
$request->attributes->set('_route', 'test_route');
3946

40-
$kernel->handle($request);
47+
$response = $kernel->handle($request);
4148

4249
$attributes = $this->storage[0]->getAttributes();
4350
$this->assertCount(1, $this->storage);
@@ -49,6 +56,12 @@ public function test_http_kernel_handle_attributes(): void
4956
$this->assertEquals(200, $attributes->get(TraceAttributes::HTTP_STATUS_CODE));
5057
$this->assertEquals('1.0', $attributes->get(TraceAttributes::HTTP_FLAVOR));
5158
$this->assertEquals(5, $attributes->get(TraceAttributes::HTTP_RESPONSE_CONTENT_LENGTH));
59+
60+
$this->assertArrayHasKey(
61+
TraceResponsePropagator::TRACERESPONSE,
62+
$response->headers->all(),
63+
'traceresponse header is present if TraceResponsePropagator is present'
64+
);
5265
}
5366

5467
public function test_http_kernel_handle_stream_response(): void
@@ -59,19 +72,31 @@ public function test_http_kernel_handle_stream_response(): void
5972
}));
6073
$this->assertCount(0, $this->storage);
6174

62-
$kernel->handle(new Request());
75+
$response = $kernel->handle(new Request());
6376
$this->assertCount(1, $this->storage);
6477
$this->assertNull($this->storage[0]->getAttributes()->get(TraceAttributes::HTTP_RESPONSE_CONTENT_LENGTH));
78+
79+
$this->assertArrayHasKey(
80+
TraceResponsePropagator::TRACERESPONSE,
81+
$response->headers->all(),
82+
'traceresponse header is present if TraceResponsePropagator is present'
83+
);
6584
}
6685

6786
public function test_http_kernel_handle_binary_file_response(): void
6887
{
6988
$kernel = $this->getHttpKernel(new EventDispatcher(), fn () => new BinaryFileResponse(__FILE__));
7089
$this->assertCount(0, $this->storage);
7190

72-
$kernel->handle(new Request());
91+
$response = $kernel->handle(new Request());
7392
$this->assertCount(1, $this->storage);
7493
$this->assertNull($this->storage[0]->getAttributes()->get(TraceAttributes::HTTP_RESPONSE_CONTENT_LENGTH));
94+
95+
$this->assertArrayHasKey(
96+
TraceResponsePropagator::TRACERESPONSE,
97+
$response->headers->all(),
98+
'traceresponse header is present if TraceResponsePropagator is present'
99+
);
75100
}
76101

77102
public function test_http_kernel_handle_with_empty_route(): void
@@ -81,19 +106,31 @@ public function test_http_kernel_handle_with_empty_route(): void
81106
$request = new Request();
82107
$request->attributes->set('_route', '');
83108

84-
$kernel->handle($request, HttpKernelInterface::MAIN_REQUEST, true);
109+
$response = $kernel->handle($request, HttpKernelInterface::MAIN_REQUEST, true);
85110
$this->assertCount(1, $this->storage);
86111
$this->assertFalse($this->storage[0]->getAttributes()->has(TraceAttributes::HTTP_ROUTE));
112+
113+
$this->assertArrayHasKey(
114+
TraceResponsePropagator::TRACERESPONSE,
115+
$response->headers->all(),
116+
'traceresponse header is present if TraceResponsePropagator is present'
117+
);
87118
}
88119

89120
public function test_http_kernel_handle_without_route(): void
90121
{
91122
$kernel = $this->getHttpKernel(new EventDispatcher());
92123
$this->assertCount(0, $this->storage);
93124

94-
$kernel->handle(new Request(), HttpKernelInterface::MAIN_REQUEST, true);
125+
$response = $kernel->handle(new Request(), HttpKernelInterface::MAIN_REQUEST, true);
95126
$this->assertCount(1, $this->storage);
96127
$this->assertFalse($this->storage[0]->getAttributes()->has(TraceAttributes::HTTP_ROUTE));
128+
129+
$this->assertArrayHasKey(
130+
TraceResponsePropagator::TRACERESPONSE,
131+
$response->headers->all(),
132+
'traceresponse header is present if TraceResponsePropagator is present'
133+
);
97134
}
98135

99136
private function getHttpKernel(EventDispatcherInterface $eventDispatcher, $controller = null, RequestStack $requestStack = null, array $arguments = []): HttpKernel

0 commit comments

Comments
 (0)