diff --git a/composer.json b/composer.json index ce0bbf139..51f8b165c 100644 --- a/composer.json +++ b/composer.json @@ -129,6 +129,7 @@ }, "spi": { "OpenTelemetry\\API\\Configuration\\Config\\ComponentProvider": [ + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Propagator\\ResponsePropagatorComposite", "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Propagator\\TextMapPropagatorB3", "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Propagator\\TextMapPropagatorB3Multi", "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Propagator\\TextMapPropagatorBaggage", @@ -179,6 +180,8 @@ "OpenTelemetry\\Tests\\Integration\\Config\\ComponentProvider\\Metrics\\AggregationResolverExplicitBucketHistogram", "OpenTelemetry\\Tests\\Integration\\Config\\ComponentProvider\\Metrics\\MetricExporterPrometheus", "OpenTelemetry\\Tests\\Integration\\Config\\ComponentProvider\\Metrics\\MetricReaderPull", + "OpenTelemetry\\Tests\\Integration\\Config\\ComponentProvider\\Propagator\\ResponsePropagatorServerTiming", + "OpenTelemetry\\Tests\\Integration\\Config\\ComponentProvider\\Propagator\\ResponsePropagatorTraceResponse", "OpenTelemetry\\Tests\\Integration\\Config\\ComponentProvider\\Propagator\\TextMapPropagatorXray", "OpenTelemetry\\Tests\\Integration\\Config\\ComponentProvider\\Propagator\\TextMapPropagatorOtTrace" ], diff --git a/examples/instrumentation/otel-sdk.yaml b/examples/instrumentation/otel-sdk.yaml index eb9a677cb..729d7ac58 100644 --- a/examples/instrumentation/otel-sdk.yaml +++ b/examples/instrumentation/otel-sdk.yaml @@ -3,6 +3,9 @@ file_format: '0.4' propagator: composite: [] +response_propagator/development: + composite: [] + tracer_provider: processors: - simple: diff --git a/examples/load_config.yaml b/examples/load_config.yaml index 528780799..808db8f4b 100644 --- a/examples/load_config.yaml +++ b/examples/load_config.yaml @@ -9,6 +9,9 @@ resource: propagators: composite: [ tracecontext, baggage ] +response_propagator/development: + composite: [ servertiming, traceresponse ] + exporters: otlp: &otlp-exporter protocol: http/protobuf diff --git a/examples/load_config_env.yaml b/examples/load_config_env.yaml index ba760b228..a3658b367 100644 --- a/examples/load_config_env.yaml +++ b/examples/load_config_env.yaml @@ -10,6 +10,9 @@ resource: propagators: composite: [ tracecontext, baggage ] +response_propagator/development: + composite: [ servertiming, traceresponse ] + attribute_limits: attribute_value_length_limit: ${OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT} attribute_count_limit: ${OTEL_ATTRIBUTE_COUNT_LIMIT} diff --git a/src/API/Globals.php b/src/API/Globals.php index f7f6e0e81..96a7c0328 100644 --- a/src/API/Globals.php +++ b/src/API/Globals.php @@ -14,6 +14,7 @@ use OpenTelemetry\API\Metrics\MeterProviderInterface; use OpenTelemetry\API\Trace\TracerProviderInterface; use OpenTelemetry\Context\Context; +use OpenTelemetry\Context\Propagation\ResponsePropagatorInterface; use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface; use function sprintf; use Throwable; @@ -35,6 +36,7 @@ public function __construct( private readonly LoggerProviderInterface $loggerProvider, private readonly EventLoggerProviderInterface $eventLoggerProvider, private readonly TextMapPropagatorInterface $propagator, + private readonly ResponsePropagatorInterface $responsePropagator, ) { } @@ -53,6 +55,14 @@ public static function propagator(): TextMapPropagatorInterface return Context::getCurrent()->get(ContextKeys::propagator()) ?? self::globals()->propagator; } + /** + * @experimental + */ + public static function responsePropagator(): ResponsePropagatorInterface + { + return Context::getCurrent()->get(ContextKeys::responsePropagator()) ?? self::globals()->responsePropagator; + } + public static function loggerProvider(): LoggerProviderInterface { return Context::getCurrent()->get(ContextKeys::loggerProvider()) ?? self::globals()->loggerProvider; @@ -107,12 +117,13 @@ private static function globals(): self $tracerProvider = $context->get(ContextKeys::tracerProvider()); $meterProvider = $context->get(ContextKeys::meterProvider()); $propagator = $context->get(ContextKeys::propagator()); + $responsePropagator = $context->get(ContextKeys::responsePropagator()); $loggerProvider = $context->get(ContextKeys::loggerProvider()); $eventLoggerProvider = $context->get(ContextKeys::eventLoggerProvider()); - assert(isset($tracerProvider, $meterProvider, $loggerProvider, $eventLoggerProvider, $propagator)); + assert(isset($tracerProvider, $meterProvider, $loggerProvider, $eventLoggerProvider, $propagator, $responsePropagator)); - return self::$globals = new self($tracerProvider, $meterProvider, $loggerProvider, $eventLoggerProvider, $propagator); + return self::$globals = new self($tracerProvider, $meterProvider, $loggerProvider, $eventLoggerProvider, $propagator, $responsePropagator); } /** diff --git a/src/API/Instrumentation/AutoInstrumentation/Context.php b/src/API/Instrumentation/AutoInstrumentation/Context.php index cb7889d51..d7b76f521 100644 --- a/src/API/Instrumentation/AutoInstrumentation/Context.php +++ b/src/API/Instrumentation/AutoInstrumentation/Context.php @@ -10,7 +10,9 @@ use OpenTelemetry\API\Metrics\Noop\NoopMeterProvider; use OpenTelemetry\API\Trace\NoopTracerProvider; use OpenTelemetry\API\Trace\TracerProviderInterface; +use OpenTelemetry\Context\Propagation\NoopResponsePropagator; use OpenTelemetry\Context\Propagation\NoopTextMapPropagator; +use OpenTelemetry\Context\Propagation\ResponsePropagatorInterface; use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface; /** @@ -23,6 +25,7 @@ public function __construct( public readonly MeterProviderInterface $meterProvider = new NoopMeterProvider(), public readonly LoggerProviderInterface $loggerProvider = new NoopLoggerProvider(), public readonly TextMapPropagatorInterface $propagator = new NoopTextMapPropagator(), + public readonly ResponsePropagatorInterface $responsePropagator = new NoopResponsePropagator(), ) { } } diff --git a/src/API/Instrumentation/Configurator.php b/src/API/Instrumentation/Configurator.php index 79df52850..728b0e9a3 100644 --- a/src/API/Instrumentation/Configurator.php +++ b/src/API/Instrumentation/Configurator.php @@ -15,7 +15,9 @@ use OpenTelemetry\Context\Context; use OpenTelemetry\Context\ContextInterface; use OpenTelemetry\Context\ImplicitContextKeyedInterface; +use OpenTelemetry\Context\Propagation\NoopResponsePropagator; use OpenTelemetry\Context\Propagation\NoopTextMapPropagator; +use OpenTelemetry\Context\Propagation\ResponsePropagatorInterface; use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface; use OpenTelemetry\Context\ScopeInterface; @@ -31,6 +33,7 @@ final class Configurator implements ImplicitContextKeyedInterface private ?TextMapPropagatorInterface $propagator = null; private ?LoggerProviderInterface $loggerProvider = null; private ?EventLoggerProviderInterface $eventLoggerProvider = null; + private ?ResponsePropagatorInterface $responsePropagator = null; private function __construct() { @@ -56,6 +59,7 @@ public static function createNoop(): Configurator ->withPropagator(new NoopTextMapPropagator()) ->withLoggerProvider(NoopLoggerProvider::getInstance()) ->withEventLoggerProvider(new NoopEventLoggerProvider()) + ->withResponsePropagator(new NoopResponsePropagator()) ; } @@ -88,6 +92,9 @@ public function storeInContext(?ContextInterface $context = null): ContextInterf if ($this->eventLoggerProvider !== null) { $context = $context->with(ContextKeys::eventLoggerProvider(), $this->eventLoggerProvider); } + if ($this->responsePropagator !== null) { + $context = $context->with(ContextKeys::responsePropagator(), $this->responsePropagator); + } return $context; } @@ -134,4 +141,12 @@ public function withEventLoggerProvider(?EventLoggerProviderInterface $eventLogg return $self; } + + public function withResponsePropagator(?ResponsePropagatorInterface $responsePropagator): Configurator + { + $self = clone $this; + $self->responsePropagator = $responsePropagator; + + return $self; + } } diff --git a/src/API/Instrumentation/ContextKeys.php b/src/API/Instrumentation/ContextKeys.php index 7e69f95d6..1d4e4bc73 100644 --- a/src/API/Instrumentation/ContextKeys.php +++ b/src/API/Instrumentation/ContextKeys.php @@ -10,6 +10,7 @@ use OpenTelemetry\API\Trace\TracerProviderInterface; use OpenTelemetry\Context\Context; use OpenTelemetry\Context\ContextKeyInterface; +use OpenTelemetry\Context\Propagation\ResponsePropagatorInterface; use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface; /** @@ -47,6 +48,16 @@ public static function propagator(): ContextKeyInterface return $instance ??= Context::createKey(TextMapPropagatorInterface::class); } + /** + * @return ContextKeyInterface + */ + public static function responsePropagator(): ContextKeyInterface + { + static $instance; + + return $instance ??= Context::createKey(ResponsePropagatorInterface::class); + } + /** * @return ContextKeyInterface */ diff --git a/src/API/Instrumentation/InstrumentationInterface.php b/src/API/Instrumentation/InstrumentationInterface.php index d67bc8d6d..d5e5cc09e 100644 --- a/src/API/Instrumentation/InstrumentationInterface.php +++ b/src/API/Instrumentation/InstrumentationInterface.php @@ -8,6 +8,7 @@ use OpenTelemetry\API\Metrics\MeterProviderInterface; use OpenTelemetry\API\Trace\TracerInterface; use OpenTelemetry\API\Trace\TracerProviderInterface; +use OpenTelemetry\Context\Propagation\ResponsePropagatorInterface; use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface; use Psr\Log\LoggerInterface; @@ -40,4 +41,8 @@ public function getMeter(): MeterInterface; public function setLogger(LoggerInterface $logger): void; public function getLogger(): LoggerInterface; + + public function setResponsePropagator(ResponsePropagatorInterface $responsePropagator): void; + + public function getResponsePropagator(): ResponsePropagatorInterface; } diff --git a/src/API/Instrumentation/InstrumentationTrait.php b/src/API/Instrumentation/InstrumentationTrait.php index b46372d1a..2feb9c6cf 100644 --- a/src/API/Instrumentation/InstrumentationTrait.php +++ b/src/API/Instrumentation/InstrumentationTrait.php @@ -11,7 +11,9 @@ use OpenTelemetry\API\Trace\NoopTracerProvider; use OpenTelemetry\API\Trace\TracerInterface; use OpenTelemetry\API\Trace\TracerProviderInterface; +use OpenTelemetry\Context\Propagation\NoopResponsePropagator; use OpenTelemetry\Context\Propagation\NoopTextMapPropagator; +use OpenTelemetry\Context\Propagation\ResponsePropagatorInterface; use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface; use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; @@ -68,6 +70,7 @@ trait InstrumentationTrait private TracerInterface $tracer; private MeterInterface $meter; private LoggerInterface $logger; + private ResponsePropagatorInterface $responsePropagator; public function __construct() { @@ -171,6 +174,16 @@ public function getLogger(): LoggerInterface return $this->logger; } + public function setResponsePropagator(ResponsePropagatorInterface $responsePropagator): void + { + $this->responsePropagator = $responsePropagator; + } + + public function getResponsePropagator(): ResponsePropagatorInterface + { + return $this->responsePropagator; + } + private function validateImplementation(): void { if (!$this instanceof InstrumentationInterface) { @@ -190,5 +203,6 @@ private function initDefaults(): void /** @phan-suppress-next-line PhanAccessMethodInternal */ $this->meter = new NoopMeter(); $this->logger = new NullLogger(); + $this->responsePropagator = new NoopResponsePropagator(); } } diff --git a/src/API/composer.json b/src/API/composer.json index 80d4f0d78..27793059d 100644 --- a/src/API/composer.json +++ b/src/API/composer.json @@ -18,7 +18,7 @@ ], "require": { "php": "^8.1", - "open-telemetry/context": "^1.0", + "open-telemetry/context": "^1.4", "psr/log": "^1.1|^2.0|^3.0", "symfony/polyfill-php82": "^1.26" }, diff --git a/src/Config/SDK/ComponentProvider/OpenTelemetrySdk.php b/src/Config/SDK/ComponentProvider/OpenTelemetrySdk.php index e4ad37352..290586551 100644 --- a/src/Config/SDK/ComponentProvider/OpenTelemetrySdk.php +++ b/src/Config/SDK/ComponentProvider/OpenTelemetrySdk.php @@ -11,8 +11,11 @@ use OpenTelemetry\API\Configuration\Context; use OpenTelemetry\Config\SDK\Configuration\Validation; use OpenTelemetry\Config\SDK\Parser\AttributesParser; +use OpenTelemetry\Context\Propagation\MultiResponsePropagator; use OpenTelemetry\Context\Propagation\MultiTextMapPropagator; +use OpenTelemetry\Context\Propagation\NoopResponsePropagator; use OpenTelemetry\Context\Propagation\NoopTextMapPropagator; +use OpenTelemetry\Context\Propagation\ResponsePropagatorInterface; use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface; use OpenTelemetry\SDK\Common\Attribute\Attributes; use OpenTelemetry\SDK\Common\Instrumentation\InstrumentationScopeFactory; @@ -87,6 +90,9 @@ final class OpenTelemetrySdk implements ComponentProvider * propagator: array{ * composite: list>, * }, + * "response_propagator/development": array{ + * composite: list>, + * }, * tracer_provider: array{ * limits: array{ * attribute_value_length_limit: ?int<0, max>, @@ -177,6 +183,13 @@ public function createPlugin(array $properties, Context $context): SdkBuilder $propagator = ($propagators === []) ? NoopTextMapPropagator::getInstance() : new MultiTextMapPropagator($propagators); $sdkBuilder->setPropagator($propagator); + $responsePropagators = []; + foreach ($properties['response_propagator/development']['composite'] as $plugin) { + $responsePropagators[] = $plugin->create($context); + } + $responsePropagator = ($responsePropagators === []) ? NoopResponsePropagator::getInstance() : new MultiResponsePropagator($responsePropagators); + $sdkBuilder->setResponsePropagator($responsePropagator); + if ($properties['disabled']) { return $sdkBuilder; } @@ -403,6 +416,7 @@ public function getConfig(ComponentProviderRegistry $registry, NodeBuilder $buil ->append($this->getTracerProviderConfig($registry, $builder)) ->append($this->getMeterProviderConfig($registry, $builder)) ->append($this->getLoggerProviderConfig($registry, $builder)) + ->append($this->getExperimentalResponsePropagatorConfig($registry, $builder)) ->end(); return $node; @@ -684,4 +698,39 @@ private function getPropagatorConfig(ComponentProviderRegistry $registry, NodeBu return $node; } + + private function getExperimentalResponsePropagatorConfig(ComponentProviderRegistry $registry, NodeBuilder $builder): ArrayNodeDefinition + { + $node = $builder->arrayNode('response_propagator/development'); + $node + ->beforeNormalization() + ->ifArray() + ->then(static function (array $value): array { + $existing = []; + foreach ($value['composite'] ?? [] as $item) { + $existing[] = key($item); + } + foreach (explode(',', $value['composite_list'] ?? '') as $name) { + $name = trim($name); + if ($name !== '' && !in_array($name, $existing)) { + $value['composite'][][$name] = null; + $existing[] = $name; + } + } + + unset($value['composite_list']); + + return $value; + }) + ->end(); + + $node + ->addDefaultsIfNotSet() + ->children() + ->append($registry->componentList('composite', ResponsePropagatorInterface::class)) + ->end() + ; + + return $node; + } } diff --git a/src/Config/SDK/ComponentProvider/Propagator/ResponsePropagatorComposite.php b/src/Config/SDK/ComponentProvider/Propagator/ResponsePropagatorComposite.php new file mode 100644 index 000000000..e48e1372d --- /dev/null +++ b/src/Config/SDK/ComponentProvider/Propagator/ResponsePropagatorComposite.php @@ -0,0 +1,40 @@ + + */ +final class ResponsePropagatorComposite implements ComponentProvider +{ + /** + * @param list> $properties + */ + #[\Override] + public function createPlugin(array $properties, Context $context): ResponsePropagatorInterface + { + $responsePropagators = []; + foreach ($properties as $plugin) { + $responsePropagators[] = $plugin->create($context); + } + + return new MultiResponsePropagator($responsePropagators); + } + + #[\Override] + public function getConfig(ComponentProviderRegistry $registry, NodeBuilder $builder): ArrayNodeDefinition + { + return $registry->componentNames('composite', ResponsePropagatorInterface::class); + } +} diff --git a/src/Config/SDK/composer.json b/src/Config/SDK/composer.json index caadbf896..a1f132538 100644 --- a/src/Config/SDK/composer.json +++ b/src/Config/SDK/composer.json @@ -18,9 +18,9 @@ ], "require": { "php": "^8.1", - "open-telemetry/api": "~1.4.0", - "open-telemetry/context": "^1.0", - "open-telemetry/sdk": "^1.0", + "open-telemetry/api": "^1.6", + "open-telemetry/context": "^1.4", + "open-telemetry/sdk": "^1.8", "symfony/config": "^5.4 || ^6.4 || ^7.0", "tbachert/spi": "^1.0.5" }, @@ -40,6 +40,7 @@ }, "spi": { "OpenTelemetry\\API\\Configuration\\Config\\ComponentProvider": [ + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Propagator\\ResponsePropagatorComposite", "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Propagator\\TextMapPropagatorB3", "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Propagator\\TextMapPropagatorB3Multi", "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Propagator\\TextMapPropagatorBaggage", diff --git a/src/Context/Propagation/MultiResponsePropagator.php b/src/Context/Propagation/MultiResponsePropagator.php new file mode 100644 index 000000000..700e3f563 --- /dev/null +++ b/src/Context/Propagation/MultiResponsePropagator.php @@ -0,0 +1,31 @@ + $responsePropagators + */ + public function __construct( + private readonly array $responsePropagators, + ) { + } + + #[\Override] + public function inject(&$carrier, ?PropagationSetterInterface $setter = null, ?ContextInterface $context = null): void + { + foreach ($this->responsePropagators as $responsePropagator) { + $responsePropagator->inject($carrier, $setter, $context); + } + } +} diff --git a/src/Context/Propagation/NoopResponsePropagator.php b/src/Context/Propagation/NoopResponsePropagator.php new file mode 100644 index 000000000..266f23258 --- /dev/null +++ b/src/Context/Propagation/NoopResponsePropagator.php @@ -0,0 +1,29 @@ +responsePropagator instanceof ResponsePropagatorInterface) { + $this->responsePropagator = ($this->responsePropagator)(); + } + + $this->responsePropagator->inject($carrier, $setter, $context); + } +} diff --git a/src/SDK/Propagation/ResponsePropagatorFactory.php b/src/SDK/Propagation/ResponsePropagatorFactory.php new file mode 100644 index 000000000..491ac7299 --- /dev/null +++ b/src/SDK/Propagation/ResponsePropagatorFactory.php @@ -0,0 +1,56 @@ + new NoopResponsePropagator(), + 1 => $this->buildResponsePropagator($responsePropagators[0]), + default => new MultiResponsePropagator($this->buildResponsePropagators($responsePropagators)), + }; + } + + /** + * @return list + */ + private function buildResponsePropagators(array $names): array + { + $responsePropagators = []; + foreach ($names as $name) { + $responsePropagators[] = $this->buildResponsePropagator($name); + } + + return $responsePropagators; + } + + private function buildResponsePropagator(string $name): ResponsePropagatorInterface + { + try { + return Registry::responsePropagator($name); + } catch (\RuntimeException $e) { + self::logWarning($e->getMessage()); + } + + return NoopResponsePropagator::getInstance(); + } +} diff --git a/src/SDK/Propagation/_register.php b/src/SDK/Propagation/_register.php index fd90da184..a6953f360 100644 --- a/src/SDK/Propagation/_register.php +++ b/src/SDK/Propagation/_register.php @@ -14,3 +14,8 @@ \OpenTelemetry\SDK\Common\Configuration\KnownValues::VALUE_NONE, \OpenTelemetry\Context\Propagation\NoopTextMapPropagator::getInstance() ); + +\OpenTelemetry\SDK\Registry::registerResponsePropagator( + \OpenTelemetry\SDK\Common\Configuration\KnownValues::VALUE_NONE, + \OpenTelemetry\Context\Propagation\NoopResponsePropagator::getInstance() +); diff --git a/src/SDK/Registry.php b/src/SDK/Registry.php index 300a8a40c..9235633f3 100644 --- a/src/SDK/Registry.php +++ b/src/SDK/Registry.php @@ -4,6 +4,7 @@ namespace OpenTelemetry\SDK; +use OpenTelemetry\Context\Propagation\ResponsePropagatorInterface; use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface; use OpenTelemetry\SDK\Common\Export\TransportFactoryInterface; use OpenTelemetry\SDK\Logs\LogRecordExporterFactoryInterface; @@ -26,6 +27,7 @@ class Registry private static array $textMapPropagators = []; private static array $logRecordExporterFactories = []; private static array $resourceDetectors = []; + private static array $responsePropagators = []; /** * @param TransportFactoryInterface|class-string $factory @@ -124,6 +126,14 @@ public static function registerResourceDetector(string $name, ResourceDetectorIn self::$resourceDetectors[$name] = $detector; } + public static function registerResponsePropagator(string $name, ResponsePropagatorInterface $responsePropagator, bool $clobber = false): void + { + if (!$clobber && array_key_exists($name, self::$responsePropagators)) { + return; + } + self::$responsePropagators[$name] = $responsePropagator; + } + public static function spanExporterFactory(string $exporter): SpanExporterFactoryInterface { if (!array_key_exists($exporter, self::$spanExporterFactories)) { @@ -195,6 +205,15 @@ public static function resourceDetector(string $name): ResourceDetectorInterface return self::$resourceDetectors[$name]; } + public static function responsePropagator(string $name): ResponsePropagatorInterface + { + if (!array_key_exists($name, self::$responsePropagators)) { + throw new RuntimeException('Response propagator not registered for: ' . $name); + } + + return self::$responsePropagators[$name]; + } + /** * @return array */ diff --git a/src/SDK/Sdk.php b/src/SDK/Sdk.php index 6bb6911ec..fc02b5a28 100644 --- a/src/SDK/Sdk.php +++ b/src/SDK/Sdk.php @@ -7,6 +7,7 @@ use OpenTelemetry\API\Logs\EventLoggerProviderInterface; use OpenTelemetry\API\Metrics\MeterProviderInterface; use OpenTelemetry\API\Trace\TracerProviderInterface; +use OpenTelemetry\Context\Propagation\ResponsePropagatorInterface; use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface; use OpenTelemetry\SDK\Common\Configuration\Configuration; use OpenTelemetry\SDK\Common\Configuration\Variables; @@ -22,6 +23,7 @@ public function __construct( private readonly LoggerProviderInterface $loggerProvider, private readonly EventLoggerProviderInterface $eventLoggerProvider, private readonly TextMapPropagatorInterface $propagator, + private readonly ResponsePropagatorInterface $responsePropagator, ) { } @@ -72,4 +74,9 @@ public function getPropagator(): TextMapPropagatorInterface { return $this->propagator; } + + public function getResponsePropagator(): ResponsePropagatorInterface + { + return $this->responsePropagator; + } } diff --git a/src/SDK/SdkAutoloader.php b/src/SDK/SdkAutoloader.php index fddcfc451..3a17e895e 100644 --- a/src/SDK/SdkAutoloader.php +++ b/src/SDK/SdkAutoloader.php @@ -28,6 +28,7 @@ use OpenTelemetry\Config\SDK\Configuration as SdkConfiguration; use OpenTelemetry\Config\SDK\Instrumentation as SdkInstrumentation; use OpenTelemetry\Context\Context; +use OpenTelemetry\Context\Propagation\ResponsePropagatorInterface; use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface; use OpenTelemetry\SDK\Common\Configuration\Configuration; use OpenTelemetry\SDK\Common\Configuration\EnvComponentLoaderRegistry; @@ -37,8 +38,10 @@ use OpenTelemetry\SDK\Logs\EventLoggerProviderFactory; use OpenTelemetry\SDK\Logs\LoggerProviderFactory; use OpenTelemetry\SDK\Metrics\MeterProviderFactory; +use OpenTelemetry\SDK\Propagation\LateBindingResponsePropagator; use OpenTelemetry\SDK\Propagation\LateBindingTextMapPropagator; use OpenTelemetry\SDK\Propagation\PropagatorFactory; +use OpenTelemetry\SDK\Propagation\ResponsePropagatorFactory; use OpenTelemetry\SDK\Resource\ResourceInfoFactory; use OpenTelemetry\SDK\Trace\AutoRootSpan; use OpenTelemetry\SDK\Trace\ExporterFactory; @@ -87,9 +90,10 @@ public static function autoload(): bool private static function environmentBasedInitializer(Configurator $configurator): Configurator { $propagator = (new PropagatorFactory())->create(); + $responsePropagator = (new ResponsePropagatorFactory())->create(); if (Sdk::isDisabled()) { //@see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/configuration/sdk-environment-variables.md#general-sdk-configuration - return $configurator->withPropagator($propagator); + return $configurator->withPropagator($propagator)->withResponsePropagator($responsePropagator); } $emitMetrics = Configuration::getBoolean(Variables::OTEL_PHP_INTERNAL_METRICS_ENABLED); @@ -116,6 +120,7 @@ private static function environmentBasedInitializer(Configurator $configurator): ->withLoggerProvider($loggerProvider) ->withEventLoggerProvider($eventLoggerProvider) ->withPropagator($propagator) + ->withResponsePropagator($responsePropagator) ; } @@ -145,6 +150,7 @@ private static function fileBasedInitializer(Configurator $configurator): Config ->withLoggerProvider($sdk->getLoggerProvider()) ->withPropagator($sdk->getPropagator()) ->withEventLoggerProvider($sdk->getEventLoggerProvider()) + ->withResponsePropagator($sdk->getResponsePropagator()) ; } @@ -167,7 +173,8 @@ private static function registerInstrumentations(): void $meterProvider = self::createLateBindingMeterProvider(); $loggerProvider = self::createLateBindingLoggerProvider(); $propagator = self::createLateBindingTextMapPropagator(); - $context = new InstrumentationContext($tracerProvider, $meterProvider, $loggerProvider, $propagator); + $responsePropagator = self::createLateBindingResponsePropagator(); + $context = new InstrumentationContext($tracerProvider, $meterProvider, $loggerProvider, $propagator, $responsePropagator); foreach (ServiceLoader::load(Instrumentation::class) as $instrumentation) { /** @var Instrumentation $instrumentation */ @@ -251,6 +258,19 @@ private static function createLateBindingTextMapPropagator(): TextMapPropagatorI }); } + private static function createLateBindingResponsePropagator(): ResponsePropagatorInterface + { + return new LateBindingResponsePropagator(static function (): ResponsePropagatorInterface { + $scope = Context::getRoot()->activate(); + + try { + return Globals::responsePropagator(); + } finally { + $scope->detach(); + } + }); + } + private static function getHookManager(): HookManagerInterface { /** @var HookManagerInterface $hookManager */ diff --git a/src/SDK/SdkBuilder.php b/src/SDK/SdkBuilder.php index f2624e947..17bdf8fb4 100644 --- a/src/SDK/SdkBuilder.php +++ b/src/SDK/SdkBuilder.php @@ -8,7 +8,9 @@ use OpenTelemetry\API\Logs\EventLoggerProviderInterface; use OpenTelemetry\API\Logs\NoopEventLoggerProvider; use OpenTelemetry\Context\Context; +use OpenTelemetry\Context\Propagation\NoopResponsePropagator; use OpenTelemetry\Context\Propagation\NoopTextMapPropagator; +use OpenTelemetry\Context\Propagation\ResponsePropagatorInterface; use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface; use OpenTelemetry\Context\ScopeInterface; use OpenTelemetry\SDK\Common\Util\ShutdownHandler; @@ -26,6 +28,7 @@ class SdkBuilder private ?LoggerProviderInterface $loggerProvider = null; private ?EventLoggerProviderInterface $eventLoggerProvider = null; private ?TextMapPropagatorInterface $propagator = null; + private ?ResponsePropagatorInterface $responsePropagator = null; private bool $autoShutdown = false; /** @@ -76,6 +79,14 @@ public function setPropagator(TextMapPropagatorInterface $propagator): self return $this; } + // @experimental + public function setResponsePropagator(ResponsePropagatorInterface $responsePropagator): self + { + $this->responsePropagator = $responsePropagator; + + return $this; + } + public function build(): Sdk { $tracerProvider = $this->tracerProvider ?? new NoopTracerProvider(); @@ -95,6 +106,7 @@ public function build(): Sdk $loggerProvider, $eventLoggerProvider, $this->propagator ?? NoopTextMapPropagator::getInstance(), + $this->responsePropagator ?? NoopResponsePropagator::getInstance(), ); } @@ -110,6 +122,7 @@ public function buildAndRegisterGlobal(): ScopeInterface ->withMeterProvider($sdk->getMeterProvider()) ->withLoggerProvider($sdk->getLoggerProvider()) ->withEventLoggerProvider($sdk->getEventLoggerProvider()) + ->withResponsePropagator($sdk->getResponsePropagator()) ->storeInContext(); return Context::storage()->attach($context); diff --git a/src/SDK/composer.json b/src/SDK/composer.json index 742dc6183..a331182c5 100644 --- a/src/SDK/composer.json +++ b/src/SDK/composer.json @@ -20,8 +20,8 @@ "php": "^8.1", "ext-json": "*", "nyholm/psr7-server": "^1.1", - "open-telemetry/api": "^1.4", - "open-telemetry/context": "^1.0", + "open-telemetry/api": "^1.6", + "open-telemetry/context": "^1.4", "open-telemetry/sem-conv": "^1.0", "php-http/discovery": "^1.14", "psr/http-client-implementation": "^1.0", diff --git a/tests/Integration/Config/ComponentProvider/Propagator/ResponsePropagatorServerTiming.php b/tests/Integration/Config/ComponentProvider/Propagator/ResponsePropagatorServerTiming.php new file mode 100644 index 000000000..be9103e63 --- /dev/null +++ b/tests/Integration/Config/ComponentProvider/Propagator/ResponsePropagatorServerTiming.php @@ -0,0 +1,38 @@ + + */ +final class ResponsePropagatorServerTiming implements ComponentProvider +{ + #[\Override] + public function createPlugin(array $properties, Context $context): ResponsePropagatorInterface + { + return new class() implements ResponsePropagatorInterface { + #[\Override] + public function inject(mixed &$carrier, ?PropagationSetterInterface $setter = null, ?ContextInterface $context = null): void + { + //no-op + } + }; + } + + #[\Override] + public function getConfig(ComponentProviderRegistry $registry, NodeBuilder $builder): ArrayNodeDefinition + { + return $builder->arrayNode('servertiming'); + } +} diff --git a/tests/Integration/Config/ComponentProvider/Propagator/ResponsePropagatorTraceResponse.php b/tests/Integration/Config/ComponentProvider/Propagator/ResponsePropagatorTraceResponse.php new file mode 100644 index 000000000..307bb3019 --- /dev/null +++ b/tests/Integration/Config/ComponentProvider/Propagator/ResponsePropagatorTraceResponse.php @@ -0,0 +1,38 @@ + + */ +final class ResponsePropagatorTraceResponse implements ComponentProvider +{ + #[\Override] + public function createPlugin(array $properties, Context $context): ResponsePropagatorInterface + { + return new class() implements ResponsePropagatorInterface { + #[\Override] + public function inject(mixed &$carrier, ?PropagationSetterInterface $setter = null, ?ContextInterface $context = null): void + { + //no-op + } + }; + } + + #[\Override] + public function getConfig(ComponentProviderRegistry $registry, NodeBuilder $builder): ArrayNodeDefinition + { + return $builder->arrayNode('traceresponse'); + } +} diff --git a/tests/Integration/Config/ConfigurationTest.php b/tests/Integration/Config/ConfigurationTest.php index e963f57f7..5365de55e 100644 --- a/tests/Integration/Config/ConfigurationTest.php +++ b/tests/Integration/Config/ConfigurationTest.php @@ -7,6 +7,7 @@ use OpenTelemetry\API\Trace\Propagation\TraceContextPropagator; use OpenTelemetry\Config\SDK\ComponentProvider\OutputStreamParser; use OpenTelemetry\Config\SDK\Configuration; +use OpenTelemetry\Context\Propagation\ResponsePropagatorInterface; use OpenTelemetry\SDK\Resource\ResourceInfo; use OpenTelemetry\SDK\Sdk; use org\bovigo\vfs\vfsStream; @@ -186,6 +187,19 @@ public function test_duplicate_propagators(): void $this->assertContains('tracestate', $propagator->fields()); } + public function test_duplicate_response_propagators(): void + { + $sdk = Configuration::parseFile(__DIR__ . '/configurations/experimental-response-propagators-duplicate.yaml')->create()->build(); + $responsePropagator = $sdk->getResponsePropagator(); + $responsePropagatorReflection = new \ReflectionClass($responsePropagator); + $responsePropagatorsProperty = $responsePropagatorReflection->getProperty('responsePropagators'); + $responsePropagatorsProperty->setAccessible(true); + $responsePropagators = $responsePropagatorsProperty->getValue($responsePropagator); + $this->assertIsArray($responsePropagators); + $this->assertCount(1, $responsePropagators, 'duplicate was removed'); + $this->assertInstanceOf(ResponsePropagatorInterface::class, $responsePropagators[0]); + } + private function getResource(Sdk $sdk): ResourceInfo { $tracer = $sdk->getTracerProvider()->getTracer('test'); diff --git a/tests/Integration/Config/configurations/anchors.yaml b/tests/Integration/Config/configurations/anchors.yaml index 0b176fc35..fe580d137 100644 --- a/tests/Integration/Config/configurations/anchors.yaml +++ b/tests/Integration/Config/configurations/anchors.yaml @@ -46,4 +46,7 @@ tracer_provider: endpoint: http://localhost:4318/v1/traces propagator: - composite: [] \ No newline at end of file + composite: [] + +response_propagator/development: + composite: [] diff --git a/tests/Integration/Config/configurations/configurators.yaml b/tests/Integration/Config/configurations/configurators.yaml index 9859ee1fa..6c7bd0087 100644 --- a/tests/Integration/Config/configurations/configurators.yaml +++ b/tests/Integration/Config/configurations/configurators.yaml @@ -3,6 +3,9 @@ file_format: '0.4' propagators: composite: [ tracecontext, baggage ] +response_propagator/development: + composite: [] + exporters: console: diff --git a/tests/Integration/Config/configurations/experimental-response-propagators-duplicate.yaml b/tests/Integration/Config/configurations/experimental-response-propagators-duplicate.yaml new file mode 100644 index 000000000..a71dcb0ac --- /dev/null +++ b/tests/Integration/Config/configurations/experimental-response-propagators-duplicate.yaml @@ -0,0 +1,12 @@ +file_format: '0.4' + +response_propagator/development: + composite_list: "servertiming,servertiming" + +tracer_provider: + processors: + +meter_provider: + readers: +logger_provider: + processors: diff --git a/tests/Integration/Config/configurations/kitchen-sink.yaml b/tests/Integration/Config/configurations/kitchen-sink.yaml index ef2e905de..772d22360 100644 --- a/tests/Integration/Config/configurations/kitchen-sink.yaml +++ b/tests/Integration/Config/configurations/kitchen-sink.yaml @@ -557,6 +557,18 @@ propagator: # Built-in propagator identifiers include: tracecontext, baggage, b3, b3multi, jaeger, ottrace. Known third party identifiers include: xray. # If the resolved list of propagators (from .composite and .composite_list) is empty, a noop propagator is used. composite_list: "tracecontext,baggage,b3,b3multi,jaeger,ottrace,xray" +# Configure response propagators. +# If omitted, a noop response propagator is used. +response_propagator/development: + # Configure the experimental response propagators in the composite experimental response propagator. Entries from .composite_list are appended to the list here with duplicates filtered out. + # Built-in experimental response propagator keys include: N/A. Known third party keys include: servertiming, traceresponse. + # If the resolved list of experimental response propagators (from .composite and .composite_list) is empty, a noop response propagator is used. + composite: + # Configure the experimental response propagators in the composite experimental response propagator. Entries are appended to .composite with duplicates filtered out. + # The value is a comma separated list of experimental response propagator identifiers matching the format of OTEL_EXPERIMENTAL_RESPONSE_PROPAGATORS. See https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/configuration/sdk-environment-variables.md#general-sdk-configuration for details. + # Built-in experimental propagator identifiers include: N/A. Known third party identifiers include: servertiming, traceresponse. + # If the resolved list of experimental propagators (from .composite and .composite_list) is empty, a noop response propagator is used. + composite_list: "servertiming,traceresponse" # Configure tracer provider. # If omitted, a noop tracer provider is used. tracer_provider: @@ -948,4 +960,4 @@ instrumentation/development: swift: # Configure the instrumentation corresponding to key "example". example: - property: "value" \ No newline at end of file + property: "value" diff --git a/tests/Unit/API/Instrumentation/AutoInstrumentation/LateBindingProviderTest.php b/tests/Unit/API/Instrumentation/AutoInstrumentation/LateBindingProviderTest.php index ad03e9db6..5e19e4891 100644 --- a/tests/Unit/API/Instrumentation/AutoInstrumentation/LateBindingProviderTest.php +++ b/tests/Unit/API/Instrumentation/AutoInstrumentation/LateBindingProviderTest.php @@ -26,6 +26,7 @@ use OpenTelemetry\API\Trace\LateBindingTracerProvider; use OpenTelemetry\API\Trace\TracerInterface; use OpenTelemetry\API\Trace\TracerProviderInterface; +use OpenTelemetry\Context\Propagation\ResponsePropagatorInterface; use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface; use OpenTelemetry\SDK\Common\Configuration\Variables; use OpenTelemetry\SDK\Metrics\NoopMeterProvider; @@ -90,6 +91,12 @@ public function getPropagator(): TextMapPropagatorInterface return self::$context->propagator; } + public function getResponsePropagator(): ResponsePropagatorInterface + { + assert(self::$context !== null); + + return self::$context->responsePropagator; + } }; $this->setEnvironmentVariable(Variables::OTEL_PHP_AUTOLOAD_ENABLED, 'true'); $tracer_accessed = false; @@ -115,16 +122,18 @@ public function getPropagator(): TextMapPropagatorInterface return $this->createMock(LoggerInterface::class); }); $propagator = $this->prophesize(TextMapPropagatorInterface::class); + $responsePropagator = $this->prophesize(ResponsePropagatorInterface::class); ServiceLoader::register(Instrumentation::class, $instrumentation::class); $this->assertTrue(SdkAutoloader::autoload()); //initializer added _after_ autoloader has run and instrumentation registered - Globals::registerInitializer(function (Configurator $configurator) use ($tracerProvider, $loggerProvider, $meterProvider, $propagator): Configurator { + Globals::registerInitializer(function (Configurator $configurator) use ($tracerProvider, $loggerProvider, $meterProvider, $propagator, $responsePropagator): Configurator { return $configurator ->withTracerProvider($tracerProvider) ->withMeterProvider($meterProvider) ->withLoggerProvider($loggerProvider) ->withPropagator($propagator->reveal()) + ->withResponsePropagator($responsePropagator->reveal()) ; }); diff --git a/tests/Unit/API/Instrumentation/InstrumentationTest.php b/tests/Unit/API/Instrumentation/InstrumentationTest.php index 84ec313c7..b34d42dda 100644 --- a/tests/Unit/API/Instrumentation/InstrumentationTest.php +++ b/tests/Unit/API/Instrumentation/InstrumentationTest.php @@ -24,7 +24,9 @@ use OpenTelemetry\API\Trace\NoopTracerProvider; use OpenTelemetry\API\Trace\TracerInterface; use OpenTelemetry\API\Trace\TracerProviderInterface; +use OpenTelemetry\Context\Propagation\NoopResponsePropagator; use OpenTelemetry\Context\Propagation\NoopTextMapPropagator; +use OpenTelemetry\Context\Propagation\ResponsePropagatorInterface; use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\MockObject\MockObject; @@ -59,6 +61,7 @@ public function test_globals_not_configured_returns_noop_instances(): void $this->assertInstanceOf(NoopTextMapPropagator::class, Globals::propagator()); $this->assertInstanceOf(NoopLoggerProvider::class, Globals::loggerProvider()); $this->assertInstanceOf(NoopEventLoggerProvider::class, Globals::eventLoggerProvider()); + $this->assertInstanceOf(NoopResponsePropagator::class, Globals::responsePropagator()); } public function test_globals_returns_configured_instances(): void @@ -68,6 +71,7 @@ public function test_globals_returns_configured_instances(): void $propagator = $this->createMock(TextMapPropagatorInterface::class); $loggerProvider = $this->createMock(LoggerProviderInterface::class); $eventLoggerProvider = $this->createMock(EventLoggerProviderInterface::class); + $responsePropagator = $this->createMock(ResponsePropagatorInterface::class); $scope = Configurator::create() ->withTracerProvider($tracerProvider) @@ -75,6 +79,7 @@ public function test_globals_returns_configured_instances(): void ->withPropagator($propagator) ->withLoggerProvider($loggerProvider) ->withEventLoggerProvider($eventLoggerProvider) + ->withResponsePropagator($responsePropagator) ->activate(); try { @@ -83,6 +88,7 @@ public function test_globals_returns_configured_instances(): void $this->assertSame($propagator, Globals::propagator()); $this->assertSame($loggerProvider, Globals::loggerProvider()); $this->assertSame($eventLoggerProvider, Globals::eventLoggerProvider()); + $this->assertSame($responsePropagator, Globals::responsePropagator()); } finally { $scope->detach(); } @@ -113,6 +119,7 @@ public function test_instrumentation_returns_configured_instances(): void $eventLoggerProvider = $this->createMock(EventLoggerProviderInterface::class); $eventLoggerProvider->method('getEventLogger')->willReturn($eventLogger); $propagator = $this->createMock(TextMapPropagatorInterface::class); + $responsePropagator = $this->createMock(ResponsePropagatorInterface::class); $scope = Configurator::create() ->withTracerProvider($tracerProvider) @@ -120,6 +127,7 @@ public function test_instrumentation_returns_configured_instances(): void ->withLoggerProvider($loggerProvider) ->withEventLoggerProvider($eventLoggerProvider) ->withPropagator($propagator) + ->withResponsePropagator($responsePropagator) ->activate(); try { diff --git a/tests/Unit/API/Instrumentation/InstrumentationTraitTest.php b/tests/Unit/API/Instrumentation/InstrumentationTraitTest.php index 486f0606f..1de7a056e 100644 --- a/tests/Unit/API/Instrumentation/InstrumentationTraitTest.php +++ b/tests/Unit/API/Instrumentation/InstrumentationTraitTest.php @@ -12,7 +12,9 @@ use OpenTelemetry\API\Trace\NoopTracer; use OpenTelemetry\API\Trace\TracerInterface; use OpenTelemetry\API\Trace\TracerProviderInterface; +use OpenTelemetry\Context\Propagation\NoopResponsePropagator; use OpenTelemetry\Context\Propagation\NoopTextMapPropagator; +use OpenTelemetry\Context\Propagation\ResponsePropagatorInterface; use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; @@ -99,6 +101,22 @@ public function test_logger(): void ); } + public function test_response_propagator(): void + { + $instrumentation = $this->createValidImplementation(); + + $this->assertInstanceOf(NoopResponsePropagator::class, $instrumentation->getResponsePropagator()); + + $responsePropagator = $this->createMock(ResponsePropagatorInterface::class); + + $instrumentation->setResponsePropagator($responsePropagator); + + $this->assertSame( + $responsePropagator, + $instrumentation->getResponsePropagator() + ); + } + public function test_activate(): void { $this->assertTrue( diff --git a/tests/Unit/Config/SDK/Configuration/ConfigurationFactoryTest.php b/tests/Unit/Config/SDK/Configuration/ConfigurationFactoryTest.php index 59a390e14..3bb6fd8b3 100644 --- a/tests/Unit/Config/SDK/Configuration/ConfigurationFactoryTest.php +++ b/tests/Unit/Config/SDK/Configuration/ConfigurationFactoryTest.php @@ -267,6 +267,9 @@ private function factory(): ConfigurationFactory { return new ConfigurationFactory( [ + new ComponentProvider\Propagator\ResponsePropagatorComposite(), + new ComponentProvider\Propagator\ResponsePropagatorServerTiming(), + new ComponentProvider\Propagator\ResponsePropagatorTraceResponse(), new ComponentProvider\Propagator\TextMapPropagatorB3(), new ComponentProvider\Propagator\TextMapPropagatorB3Multi(), new ComponentProvider\Propagator\TextMapPropagatorBaggage(), diff --git a/tests/Unit/Config/SDK/Configuration/ExampleSdk/OpenTelemetryConfiguration.php b/tests/Unit/Config/SDK/Configuration/ExampleSdk/OpenTelemetryConfiguration.php index e049894d5..1670e24d6 100644 --- a/tests/Unit/Config/SDK/Configuration/ExampleSdk/OpenTelemetryConfiguration.php +++ b/tests/Unit/Config/SDK/Configuration/ExampleSdk/OpenTelemetryConfiguration.php @@ -16,6 +16,7 @@ use OpenTelemetry\API\Configuration\Config\ComponentProviderRegistry; use OpenTelemetry\API\Configuration\Context; use OpenTelemetry\Config\SDK\Configuration\Validation; +use OpenTelemetry\Context\Propagation\ResponsePropagatorInterface; use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface; use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; use Symfony\Component\Config\Definition\Builder\NodeBuilder; @@ -35,6 +36,7 @@ final class OpenTelemetryConfiguration implements ComponentProvider * attribute_count_limit: int<0, max>, * }, * propagator: ?ComponentPlugin, + * response_propagator/development: ?ComponentPlugin, * tracer_provider: array{ * limits: array{ * attribute_value_length_limit: ?int<0, max>, @@ -102,6 +104,7 @@ public function getConfig(ComponentProviderRegistry $registry, NodeBuilder $buil ->append($this->getTracerProviderConfig($registry, $builder)) ->append($this->getMeterProviderConfig($registry, $builder)) ->append($this->getLoggerProviderConfig($registry, $builder)) + ->append($registry->component('response_propagator/development', ResponsePropagatorInterface::class)) ->end(); return $node; diff --git a/tests/Unit/Config/SDK/Configuration/ExampleSdk/Propagator/ResponsePropagatorComposite.php b/tests/Unit/Config/SDK/Configuration/ExampleSdk/Propagator/ResponsePropagatorComposite.php new file mode 100644 index 000000000..9f467285e --- /dev/null +++ b/tests/Unit/Config/SDK/Configuration/ExampleSdk/Propagator/ResponsePropagatorComposite.php @@ -0,0 +1,32 @@ +> $properties + */ + #[\Override] + public function createPlugin(array $properties, Context $context): ResponsePropagatorInterface + { + throw new BadMethodCallException('not implemented'); + } + + #[\Override] + public function getConfig(ComponentProviderRegistry $registry, NodeBuilder $builder): ArrayNodeDefinition + { + return $registry->componentNames('composite', ResponsePropagatorInterface::class); + } +} diff --git a/tests/Unit/Config/SDK/Configuration/ExampleSdk/Propagator/ResponsePropagatorServerTiming.php b/tests/Unit/Config/SDK/Configuration/ExampleSdk/Propagator/ResponsePropagatorServerTiming.php new file mode 100644 index 000000000..3fe3a4194 --- /dev/null +++ b/tests/Unit/Config/SDK/Configuration/ExampleSdk/Propagator/ResponsePropagatorServerTiming.php @@ -0,0 +1,32 @@ +componentNames('servertiming', ResponsePropagatorInterface::class); + } +} diff --git a/tests/Unit/Config/SDK/Configuration/ExampleSdk/Propagator/ResponsePropagatorTraceResponse.php b/tests/Unit/Config/SDK/Configuration/ExampleSdk/Propagator/ResponsePropagatorTraceResponse.php new file mode 100644 index 000000000..a4a04d83d --- /dev/null +++ b/tests/Unit/Config/SDK/Configuration/ExampleSdk/Propagator/ResponsePropagatorTraceResponse.php @@ -0,0 +1,32 @@ +componentNames('traceresponse', ResponsePropagatorInterface::class); + } +} diff --git a/tests/Unit/Config/SDK/Configuration/configurations/kitchen-sink.yaml b/tests/Unit/Config/SDK/Configuration/configurations/kitchen-sink.yaml index 41e9cfafc..09617fca2 100644 --- a/tests/Unit/Config/SDK/Configuration/configurations/kitchen-sink.yaml +++ b/tests/Unit/Config/SDK/Configuration/configurations/kitchen-sink.yaml @@ -232,6 +232,12 @@ meter_provider: propagator: composite: [tracecontext, baggage, b3, b3multi, jaeger, xray, ottrace] +# Configure response propagators. +# +# Environment variable: OTEL_EXPERIMENTAL_RESPONSE_PROPAGATORS +response_propagator/development: + composite: [servertiming, traceresponse] + # Configure tracer provider. tracer_provider: # Configure span processors. diff --git a/tests/Unit/Context/Propagation/MultiResponsePropagatorTest.php b/tests/Unit/Context/Propagation/MultiResponsePropagatorTest.php new file mode 100644 index 000000000..490fcde6a --- /dev/null +++ b/tests/Unit/Context/Propagation/MultiResponsePropagatorTest.php @@ -0,0 +1,49 @@ +propagator1 = Mockery::mock(ResponsePropagatorInterface::class); + $this->propagator2 = Mockery::mock(ResponsePropagatorInterface::class); + $this->propagator3 = Mockery::mock(ResponsePropagatorInterface::class); + } + + public function test_inject_delegates(): void + { + $carrier = []; + $context = Context::getRoot(); + + $this->propagator1->expects('inject')->with($carrier, null, $context); + $this->propagator2->expects('inject')->with($carrier, null, $context); + $this->propagator3->expects('inject')->with($carrier, null, $context); + + (new MultiResponsePropagator([ + $this->propagator1, + $this->propagator2, + $this->propagator3, + ]))->inject($carrier, null, $context); + } +} diff --git a/tests/Unit/Context/Propagation/NoopResponsePropagatorTest.php b/tests/Unit/Context/Propagation/NoopResponsePropagatorTest.php new file mode 100644 index 000000000..59ad1cae6 --- /dev/null +++ b/tests/Unit/Context/Propagation/NoopResponsePropagatorTest.php @@ -0,0 +1,20 @@ +inject($carrier); + $this->assertEmpty($carrier); + } +} diff --git a/tests/Unit/SDK/FactoryRegistryTest.php b/tests/Unit/SDK/FactoryRegistryTest.php index d58842375..d823e4da9 100644 --- a/tests/Unit/SDK/FactoryRegistryTest.php +++ b/tests/Unit/SDK/FactoryRegistryTest.php @@ -4,6 +4,7 @@ namespace OpenTelemetry\Tests\Unit\SDK; +use OpenTelemetry\Context\Propagation\ResponsePropagatorInterface; use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface; use OpenTelemetry\SDK\Common\Export\TransportFactoryInterface; use OpenTelemetry\SDK\Logs\LogRecordExporterFactoryInterface; @@ -106,6 +107,20 @@ public static function textMapPropagator(): array ]; } + #[DataProvider('responsePropagator')] + public function test_default_response_propagator(string $name): void + { + $responsePropagator = Registry::responsePropagator($name); + $this->assertInstanceOf(ResponsePropagatorInterface::class, $responsePropagator); + } + + public static function responsePropagator(): array + { + return [ + ['none'], + ]; + } + #[DataProvider('invalidFactoryProvider')] public function test_register_invalid_transport_factory($factory): void { diff --git a/tests/Unit/SDK/Propagation/ResponsePropagatorFactoryTest.php b/tests/Unit/SDK/Propagation/ResponsePropagatorFactoryTest.php new file mode 100644 index 000000000..84dbfc3f5 --- /dev/null +++ b/tests/Unit/SDK/Propagation/ResponsePropagatorFactoryTest.php @@ -0,0 +1,48 @@ +setEnvironmentVariable(Variables::OTEL_EXPERIMENTAL_RESPONSE_PROPAGATORS, $responsePropagators); + $responsePropagator = (new ResponsePropagatorFactory())->create(); + $this->assertInstanceOf($expected, $responsePropagator); + } + + public static function responsePropagatorsProvider(): array + { + return [ + [KnownValues::VALUE_NONE, NoopResponsePropagator::class], + ['unknown', NoopResponsePropagator::class], + ]; + } +} diff --git a/tests/Unit/SDK/SdkAutoloaderTest.php b/tests/Unit/SDK/SdkAutoloaderTest.php index 540dee5d2..5eda46db6 100644 --- a/tests/Unit/SDK/SdkAutoloaderTest.php +++ b/tests/Unit/SDK/SdkAutoloaderTest.php @@ -11,7 +11,9 @@ use OpenTelemetry\API\Logs\NoopLoggerProvider; use OpenTelemetry\API\Metrics\Noop\NoopMeterProvider; use OpenTelemetry\API\Trace\NoopTracerProvider; +use OpenTelemetry\Context\Propagation\NoopResponsePropagator; use OpenTelemetry\Context\Propagation\NoopTextMapPropagator; +use OpenTelemetry\Context\Propagation\ResponsePropagatorInterface; use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface; use OpenTelemetry\SDK\Common\Configuration\Variables; use OpenTelemetry\SDK\SdkAutoloader; @@ -73,6 +75,7 @@ public function test_noop_if_disabled(): void $this->assertInstanceOf(NoopLoggerProvider::class, Globals::loggerProvider()); $this->assertInstanceOf(NoopEventLoggerProvider::class, Globals::eventLoggerProvider()); $this->assertInstanceOf(NoopTextMapPropagator::class, Globals::propagator(), 'propagator not initialized by disabled autoloader'); + $this->assertInstanceOf(NoopResponsePropagator::class, Globals::responsePropagator(), 'response propagator not initialized by disabled autoloader'); } public function test_sdk_disabled_does_not_disable_propagator(): void @@ -83,6 +86,7 @@ public function test_sdk_disabled_does_not_disable_propagator(): void $this->assertNotInstanceOf(NoopTextMapPropagator::class, Globals::propagator()); $this->assertInstanceOf(NoopMeterProvider::class, Globals::meterProvider()); $this->assertInstanceOf(NoopTracerProvider::class, Globals::tracerProvider()); + $this->assertInstanceOf(NoopResponsePropagator::class, Globals::responsePropagator()); } public function test_enabled_by_configuration(): void @@ -93,6 +97,7 @@ public function test_enabled_by_configuration(): void $this->assertNotInstanceOf(NoopMeterProvider::class, Globals::meterProvider()); $this->assertNotInstanceOf(NoopTracerProvider::class, Globals::tracerProvider()); $this->assertNotInstanceOf(NoopLoggerProvider::class, Globals::loggerProvider()); + $this->assertInstanceOf(NoopResponsePropagator::class, Globals::responsePropagator()); } public function test_exclude_urls_without_request_uri(): void @@ -175,10 +180,12 @@ public function test_autoload_with_late_globals_initializer(): void $this->assertTrue(SdkAutoloader::autoload()); //SDK is configured, but globals have not been initialized yet, so we can add more initializers $propagator = $this->createMock(TextMapPropagatorInterface::class); - Globals::registerInitializer(function (Configurator $configurator) use ($propagator) { - return $configurator->withPropagator($propagator); + $responsePropagator = $this->createMock(ResponsePropagatorInterface::class); + Globals::registerInitializer(function (Configurator $configurator) use ($propagator, $responsePropagator) { + return $configurator->withPropagator($propagator)->withResponsePropagator($responsePropagator); }); $this->assertSame($propagator, Globals::propagator()); + $this->assertSame($responsePropagator, Globals::responsePropagator()); } } diff --git a/tests/Unit/SDK/SdkBuilderTest.php b/tests/Unit/SDK/SdkBuilderTest.php index 766356098..3340bba5f 100644 --- a/tests/Unit/SDK/SdkBuilderTest.php +++ b/tests/Unit/SDK/SdkBuilderTest.php @@ -6,6 +6,7 @@ use OpenTelemetry\API\Globals; use OpenTelemetry\API\Logs\EventLoggerProviderInterface; +use OpenTelemetry\Context\Propagation\ResponsePropagatorInterface; use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface; use OpenTelemetry\SDK\Logs\LoggerProviderInterface; use OpenTelemetry\SDK\Metrics\MeterProviderInterface; @@ -22,6 +23,7 @@ class SdkBuilderTest extends TestCase private MeterProviderInterface $meterProvider; private LoggerProviderInterface $loggerProvider; private EventLoggerProviderInterface $eventLoggerProvider; + private ResponsePropagatorInterface $responsePropagator; private SdkBuilder $builder; #[\Override] @@ -32,11 +34,13 @@ public function setUp(): void $this->meterProvider = $this->createMock(MeterProviderInterface::class); $this->loggerProvider = $this->createMock(LoggerProviderInterface::class); $this->eventLoggerProvider = $this->createMock(EventLoggerProviderInterface::class); + $this->responsePropagator = $this->createMock(ResponsePropagatorInterface::class); $this->builder = (new SdkBuilder()) ->setMeterProvider($this->meterProvider) ->setLoggerProvider($this->loggerProvider) ->setEventLoggerProvider($this->eventLoggerProvider) ->setPropagator($this->propagator) + ->setResponsePropagator($this->responsePropagator) ->setTracerProvider($this->tracerProvider) ->setAutoShutdown(true); } @@ -49,6 +53,7 @@ public function test_build(): void $this->assertSame($this->tracerProvider, $sdk->getTracerProvider()); $this->assertSame($this->loggerProvider, $sdk->getLoggerProvider()); $this->assertSame($this->eventLoggerProvider, $sdk->getEventLoggerProvider()); + $this->assertSame($this->responsePropagator, $sdk->getResponsePropagator()); } public function test_build_and_register_global(): void @@ -59,6 +64,7 @@ public function test_build_and_register_global(): void $this->assertSame($this->tracerProvider, Globals::tracerProvider()); $this->assertSame($this->loggerProvider, Globals::loggerProvider()); $this->assertSame($this->eventLoggerProvider, Globals::eventLoggerProvider()); + $this->assertSame($this->responsePropagator, Globals::responsePropagator()); $scope->detach(); } } diff --git a/tests/Unit/SDK/SdkTest.php b/tests/Unit/SDK/SdkTest.php index a4834d7f9..81ed756d6 100644 --- a/tests/Unit/SDK/SdkTest.php +++ b/tests/Unit/SDK/SdkTest.php @@ -5,6 +5,7 @@ namespace OpenTelemetry\Tests\Unit\SDK; use OpenTelemetry\API\Logs\EventLoggerProviderInterface; +use OpenTelemetry\Context\Propagation\ResponsePropagatorInterface; use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface; use OpenTelemetry\SDK\Common\Configuration\Variables; use OpenTelemetry\SDK\Logs\LoggerProviderInterface; @@ -27,6 +28,7 @@ class SdkTest extends TestCase private TracerProviderInterface $tracerProvider; private LoggerProviderInterface $loggerProvider; private EventLoggerProviderInterface $eventLoggerProvider; + private ResponsePropagatorInterface $responsePropagator; #[\Override] public function setUp(): void @@ -36,6 +38,7 @@ public function setUp(): void $this->tracerProvider = $this->createMock(TracerProviderInterface::class); $this->loggerProvider = $this->createMock(LoggerProviderInterface::class); $this->eventLoggerProvider = $this->createMock(EventLoggerProviderInterface::class); + $this->responsePropagator = $this->createMock(ResponsePropagatorInterface::class); } #[\Override] @@ -92,11 +95,12 @@ public function test_builder(): void public function test_getters(): void { - $sdk = new Sdk($this->tracerProvider, $this->meterProvider, $this->loggerProvider, $this->eventLoggerProvider, $this->propagator); + $sdk = new Sdk($this->tracerProvider, $this->meterProvider, $this->loggerProvider, $this->eventLoggerProvider, $this->propagator, $this->responsePropagator); $this->assertSame($this->propagator, $sdk->getPropagator()); $this->assertSame($this->meterProvider, $sdk->getMeterProvider()); $this->assertSame($this->tracerProvider, $sdk->getTracerProvider()); $this->assertSame($this->loggerProvider, $sdk->getLoggerProvider()); $this->assertSame($this->eventLoggerProvider, $sdk->getEventLoggerProvider()); + $this->assertSame($this->responsePropagator, $sdk->getResponsePropagator()); } } diff --git a/tests/Unit/SDK/fixtures/otel-sdk.yaml b/tests/Unit/SDK/fixtures/otel-sdk.yaml index 130efb18a..37024fef0 100644 --- a/tests/Unit/SDK/fixtures/otel-sdk.yaml +++ b/tests/Unit/SDK/fixtures/otel-sdk.yaml @@ -11,6 +11,9 @@ tracer_provider: propagator: composite: [] +response_propagator/development: + composite: [] + instrumentation/development: php: example_instrumentation: