Skip to content
Merged
3 changes: 3 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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"
],
Expand Down
3 changes: 3 additions & 0 deletions examples/instrumentation/otel-sdk.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ file_format: '0.4'
propagator:
composite: []

response_propagator:
composite: []

tracer_provider:
processors:
- simple:
Expand Down
3 changes: 3 additions & 0 deletions examples/load_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ resource:
propagators:
composite: [ tracecontext, baggage ]

response_propagator:
composite: [ servertiming, traceresponse ]

exporters:
otlp: &otlp-exporter
protocol: http/protobuf
Expand Down
3 changes: 3 additions & 0 deletions examples/load_config_env.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ resource:
propagators:
composite: [ tracecontext, baggage ]

response_propagator:
composite: [ servertiming, traceresponse ]

attribute_limits:
attribute_value_length_limit: ${OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT}
attribute_count_limit: ${OTEL_ATTRIBUTE_COUNT_LIMIT}
Expand Down
12 changes: 10 additions & 2 deletions src/API/Globals.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -35,6 +36,7 @@ public function __construct(
private readonly LoggerProviderInterface $loggerProvider,
private readonly EventLoggerProviderInterface $eventLoggerProvider,
private readonly TextMapPropagatorInterface $propagator,
private readonly ResponsePropagatorInterface $responsePropagator,
) {
}

Expand All @@ -53,6 +55,11 @@ public static function propagator(): TextMapPropagatorInterface
return Context::getCurrent()->get(ContextKeys::propagator()) ?? self::globals()->propagator;
}

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;
Expand Down Expand Up @@ -107,12 +114,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);
}

/**
Expand Down
3 changes: 3 additions & 0 deletions src/API/Instrumentation/AutoInstrumentation/Context.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand All @@ -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(),
) {
}
}
15 changes: 15 additions & 0 deletions src/API/Instrumentation/Configurator.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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()
{
Expand All @@ -56,6 +59,7 @@ public static function createNoop(): Configurator
->withPropagator(new NoopTextMapPropagator())
->withLoggerProvider(NoopLoggerProvider::getInstance())
->withEventLoggerProvider(new NoopEventLoggerProvider())
->withResponsePropagator(new NoopResponsePropagator())
;
}

Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
}
11 changes: 11 additions & 0 deletions src/API/Instrumentation/ContextKeys.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -47,6 +48,16 @@ public static function propagator(): ContextKeyInterface
return $instance ??= Context::createKey(TextMapPropagatorInterface::class);
}

/**
* @return ContextKeyInterface<ResponsePropagatorInterface>
*/
public static function responsePropagator(): ContextKeyInterface
{
static $instance;

return $instance ??= Context::createKey(ResponsePropagatorInterface::class);
}

/**
* @return ContextKeyInterface<LoggerProviderInterface>
*/
Expand Down
5 changes: 5 additions & 0 deletions src/API/Instrumentation/InstrumentationInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
}
14 changes: 14 additions & 0 deletions src/API/Instrumentation/InstrumentationTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -68,6 +70,7 @@ trait InstrumentationTrait
private TracerInterface $tracer;
private MeterInterface $meter;
private LoggerInterface $logger;
private ResponsePropagatorInterface $responsePropagator;

public function __construct()
{
Expand Down Expand Up @@ -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) {
Expand All @@ -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();
}
}
52 changes: 52 additions & 0 deletions src/Config/SDK/ComponentProvider/OpenTelemetrySdk.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -87,6 +90,9 @@ final class OpenTelemetrySdk implements ComponentProvider
* propagator: array{
* composite: list<ComponentPlugin<TextMapPropagatorInterface>>,
* },
* response_propagator: array{
* composite: list<ComponentPlugin<ResponsePropagatorInterface>>,
* },
* tracer_provider: array{
* limits: array{
* attribute_value_length_limit: ?int<0, max>,
Expand Down Expand Up @@ -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']['composite'] as $plugin) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. I agree that ideally this is how it would work, and the config spec allows extra entries here, but I think we should be cautious about adding non-spec entries here. At the least, it should be experimental_response_propagator. My main concern is incompatibility if/when something is added to spec.
The config repo may accept a PR adding it as experimental and optional?

Copy link
Contributor Author

@jerrytfleung jerrytfleung Sep 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Changed to use response_propagator/development

$responsePropagators[] = $plugin->create($context);
}
$responsePropagator = ($responsePropagators === []) ? NoopResponsePropagator::getInstance() : new MultiResponsePropagator($responsePropagators);
$sdkBuilder->setResponsePropagator($responsePropagator);

if ($properties['disabled']) {
return $sdkBuilder;
}
Expand Down Expand Up @@ -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->getResponsePropagatorConfig($registry, $builder))
->end();

return $node;
Expand Down Expand Up @@ -684,4 +698,42 @@ private function getPropagatorConfig(ComponentProviderRegistry $registry, NodeBu

return $node;
}

private function getResponsePropagatorConfig(ComponentProviderRegistry $registry, NodeBuilder $builder): ArrayNodeDefinition
{
$node = $builder->arrayNode('response_propagator');
$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))
// ->arrayNode('composite_list')
// ->scalarPrototype()->end()
// ->end()
->end()
;

return $node;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\Config\SDK\ComponentProvider\Propagator;

use OpenTelemetry\API\Configuration\Config\ComponentPlugin;
use OpenTelemetry\API\Configuration\Config\ComponentProvider;
use OpenTelemetry\API\Configuration\Config\ComponentProviderRegistry;
use OpenTelemetry\API\Configuration\Context;
use OpenTelemetry\Context\Propagation\MultiResponsePropagator;
use OpenTelemetry\Context\Propagation\ResponsePropagatorInterface;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\NodeBuilder;

/**
* @implements ComponentProvider<ResponsePropagatorInterface>
*/
final class ResponsePropagatorComposite implements ComponentProvider
{
/**
* @param list<ComponentPlugin<ResponsePropagatorInterface>> $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);
}
}
1 change: 1 addition & 0 deletions src/Config/SDK/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Loading
Loading