diff --git a/src/Instrumentation/Slim/composer.json b/src/Instrumentation/Slim/composer.json index 984bc39c..fc0577ad 100644 --- a/src/Instrumentation/Slim/composer.json +++ b/src/Instrumentation/Slim/composer.json @@ -15,6 +15,9 @@ "open-telemetry/api": "^1.0", "slim/slim": "^4" }, + "suggest": { + "open-telemetry/opentelemetry-propagation-traceresponse": "Automatically propagate the context to the client." + }, "require-dev": { "friendsofphp/php-cs-fixer": "^3", "mockery/mockery": "^1.5", @@ -27,7 +30,10 @@ "psalm/plugin-phpunit": "^0.16", "open-telemetry/sdk": "^1.0", "phpunit/phpunit": "^9.5", - "vimeo/psalm": "^4.0" + "vimeo/psalm": "^4.0", + "open-telemetry/opentelemetry-propagation-traceresponse": "*", + "symfony/http-client": "^6.0", + "guzzlehttp/promises": "^1.5" }, "autoload": { "psr-4": { @@ -41,5 +47,10 @@ "psr-4": { "OpenTelemetry\\Tests\\Instrumentation\\Slim\\": "tests/" } + }, + "config": { + "allow-plugins": { + "php-http/discovery": true + } } } diff --git a/src/Instrumentation/Slim/src/SlimInstrumentation.php b/src/Instrumentation/Slim/src/SlimInstrumentation.php index d87ede73..354b6d31 100644 --- a/src/Instrumentation/Slim/src/SlimInstrumentation.php +++ b/src/Instrumentation/Slim/src/SlimInstrumentation.php @@ -11,6 +11,7 @@ use OpenTelemetry\API\Trace\SpanKind; use OpenTelemetry\API\Trace\StatusCode; use OpenTelemetry\Context\Context; +use OpenTelemetry\Context\Propagation\ArrayAccessGetterSetter; use function OpenTelemetry\Instrumentation\hook; use OpenTelemetry\SemConv\TraceAttributes; use Psr\Http\Message\ResponseInterface; @@ -61,10 +62,11 @@ public static function register(): void return [$request]; }, + /** @suppress PhanPluginInconsistentReturnFunction */ post: static function (App $app, array $params, ?ResponseInterface $response, ?Throwable $exception) { $scope = Context::storage()->scope(); if (!$scope) { - return; + return $response; } $scope->detach(); $span = Span::fromContext($scope->context()); @@ -79,9 +81,26 @@ public static function register(): void $span->setAttribute(TraceAttributes::HTTP_STATUS_CODE, $response->getStatusCode()); $span->setAttribute(TraceAttributes::HTTP_FLAVOR, $response->getProtocolVersion()); $span->setAttribute(TraceAttributes::HTTP_RESPONSE_CONTENT_LENGTH, $response->getHeaderLine('Content-Length')); + + // Propagate traceresponse header to response, if TraceResponsePropagator is present + if (class_exists('OpenTelemetry\Contrib\Propagation\TraceResponse\TraceResponsePropagator')) { + $carrier = []; + $prop = new \OpenTelemetry\Contrib\Propagation\TraceResponse\TraceResponsePropagator(); + $prop->inject($carrier, ArrayAccessGetterSetter::getInstance(), $scope->context()); + + foreach ($carrier as $name => $value) { + $response = $response->withHeader($name, $value); + } + + $span->end(); + + return $response; + } } $span->end(); + + return $response; } ); diff --git a/src/Instrumentation/Slim/tests/Integration/SlimInstrumentationTest.php b/src/Instrumentation/Slim/tests/Integration/SlimInstrumentationTest.php index b3cc2b25..658badcf 100644 --- a/src/Instrumentation/Slim/tests/Integration/SlimInstrumentationTest.php +++ b/src/Instrumentation/Slim/tests/Integration/SlimInstrumentationTest.php @@ -10,7 +10,7 @@ use Nyholm\Psr7\ServerRequest; use OpenTelemetry\API\Common\Instrumentation\Configurator; use OpenTelemetry\Context\ScopeInterface; -use OpenTelemetry\SDK\Trace\ImmutableSpan; +use OpenTelemetry\Contrib\Propagation\TraceResponse\TraceResponsePropagator; use OpenTelemetry\SDK\Trace\SpanExporter\InMemoryExporter; use OpenTelemetry\SDK\Trace\SpanProcessor\SimpleSpanProcessor; use OpenTelemetry\SDK\Trace\TracerProvider; @@ -67,10 +67,13 @@ public function performRouting(ServerRequestInterface $request): ServerRequestIn $this->createMock(ResponseInterface::class), $routingMiddleware ); - $app->handle($request->withAttribute(RouteContext::ROUTE, $route)); + $response = $app->handle($request->withAttribute(RouteContext::ROUTE, $route)); + $this->assertCount(1, $this->storage); $span = $this->storage->offsetGet(0); // @var ImmutableSpan $span $this->assertSame($expected, $span->getName()); + + $this->assertTrue($response->hasHeader(TraceResponsePropagator::TRACERESPONSE), 'traceresponse header is added'); } /** @@ -125,14 +128,19 @@ public function performRouting(ServerRequestInterface $request): ServerRequestIn $routingMiddleware ); + $response = null; + try { - $app->handle($request); + $response = $app->handle($request); } catch (\Exception $e) { $this->assertSame('routing failed', $e->getMessage()); } $this->assertCount(1, $this->storage); $span = $this->storage->offsetGet(0); // @var ImmutableSpan $span $this->assertSame('HTTP GET', $span->getName(), 'span name was not updated because routing failed'); + + /** @psalm-suppress PossiblyNullReference */ + $this->assertTrue($response->hasHeader(TraceResponsePropagator::TRACERESPONSE), 'traceresponse header is added'); } public function createMockStrategy(): InvocationStrategyInterface