diff --git a/src/SDK/Registry.php b/src/SDK/Registry.php index e4b9526e9..74fc1ca6c 100644 --- a/src/SDK/Registry.php +++ b/src/SDK/Registry.php @@ -5,7 +5,6 @@ namespace OpenTelemetry\SDK; use Nevay\SPI\ServiceLoader; -use OpenTelemetry\Context\Propagation\TextMapPropagatorFactoryInterface; use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface; use OpenTelemetry\SDK\Common\Export\TransportFactoryInterface; use OpenTelemetry\SDK\Logs\LogRecordExporterFactoryInterface; @@ -24,8 +23,6 @@ */ class Registry { - private static bool $initialized = false; - /** @var array $spanExporterFactories */ private static array $spanExporterFactories = []; /** @var array $transportFactories */ @@ -40,27 +37,11 @@ class Registry /** @var array $resourceDetectors */ private static array $resourceDetectors = []; - public static function maybeInitFromSpi(): void - { - if (self::$initialized || Sdk::useSpiRegistry() === false) { - return; - } - - self::$spanExporterFactories = self::initFactories(SpanExporterFactoryInterface::class); - self::$logRecordExporterFactories = self::initFactories(LogRecordExporterFactoryInterface::class); - self::$metricExporterFactories = self::initFactories(MetricExporterFactoryInterface::class); - self::$transportFactories = self::initFactories(TransportFactoryInterface::class); - self::$resourceDetectors = self::initFactoryInstances(ResourceDetectorFactoryInterface::class); - self::$textMapPropagators = self::initFactoryInstances(TextMapPropagatorFactoryInterface::class); - - self::$initialized = true; - } - /** * @param class-string $class * @phan-suppress PhanTypeNonVarPassByRef */ - private static function initFactories(string $class): array + private static function getFactories(string $class): array { $factories = iterator_to_array(ServiceLoader::load($class)); array_multisort( @@ -80,7 +61,7 @@ private static function initFactories(string $class): array * @param class-string $class * @phan-suppress PhanTypeNonVarPassByRef */ - private static function initFactoryInstances(string $class): array + private static function getFactoryInstances(string $class): array { $factories = iterator_to_array(ServiceLoader::load($class)); array_multisort( @@ -216,11 +197,15 @@ public static function registerResourceDetector(string $name, ResourceDetectorIn */ public static function spanExporterFactory(string $exporter): SpanExporterFactoryInterface { - self::maybeInitFromSpi(); - if (!array_key_exists($exporter, self::$spanExporterFactories)) { + if (Sdk::useSpiRegistry()) { + $factories = self::getFactories(SpanExporterFactoryInterface::class); + } else { + $factories = self::$spanExporterFactories; + } + if (!array_key_exists($exporter, $factories)) { throw new RuntimeException('Span exporter factory not defined for: ' . $exporter); } - $class = self::$spanExporterFactories[$exporter]; + $class = $factories[$exporter]; $factory = (is_callable($class)) ? $class : new $class(); assert($factory instanceof SpanExporterFactoryInterface); @@ -232,11 +217,15 @@ public static function spanExporterFactory(string $exporter): SpanExporterFactor */ public static function logRecordExporterFactory(string $exporter): LogRecordExporterFactoryInterface { - self::maybeInitFromSpi(); - if (!array_key_exists($exporter, self::$logRecordExporterFactories)) { + if (Sdk::useSpiRegistry()) { + $factories = self::getFactories(LogRecordExporterFactoryInterface::class); + } else { + $factories = self::$logRecordExporterFactories; + } + if (!array_key_exists($exporter, $factories)) { throw new RuntimeException('LogRecord exporter factory not defined for: ' . $exporter); } - $class = self::$logRecordExporterFactories[$exporter]; + $class = $factories[$exporter]; $factory = (is_callable($class)) ? $class : new $class(); assert($factory instanceof LogRecordExporterFactoryInterface); @@ -250,12 +239,16 @@ public static function logRecordExporterFactory(string $exporter): LogRecordExpo */ public static function transportFactory(string $protocol): TransportFactoryInterface { - self::maybeInitFromSpi(); + if (Sdk::useSpiRegistry()) { + $factories = self::getFactories(TransportFactoryInterface::class); + } else { + $factories = self::$transportFactories; + } $protocol = explode('/', $protocol)[0]; - if (!array_key_exists($protocol, self::$transportFactories)) { + if (!array_key_exists($protocol, $factories)) { throw new RuntimeException('Transport factory not defined for protocol: ' . $protocol); } - $class = self::$transportFactories[$protocol]; + $class = $factories[$protocol]; $factory = (is_callable($class)) ? $class : new $class(); assert($factory instanceof TransportFactoryInterface); @@ -267,11 +260,15 @@ public static function transportFactory(string $protocol): TransportFactoryInter */ public static function metricExporterFactory(string $exporter): MetricExporterFactoryInterface { - self::maybeInitFromSpi(); - if (!array_key_exists($exporter, self::$metricExporterFactories)) { + if (Sdk::useSpiRegistry()) { + $factories = self::getFactories(MetricExporterFactoryInterface::class); + } else { + $factories = self::$metricExporterFactories; + } + if (!array_key_exists($exporter, $factories)) { throw new RuntimeException('Metric exporter factory not registered for protocol: ' . $exporter); } - $class = self::$metricExporterFactories[$exporter]; + $class = $factories[$exporter]; $factory = (is_callable($class)) ? $class : new $class(); assert($factory instanceof MetricExporterFactoryInterface); @@ -280,22 +277,30 @@ public static function metricExporterFactory(string $exporter): MetricExporterFa public static function textMapPropagator(string $name): TextMapPropagatorInterface { - self::maybeInitFromSpi(); - if (!array_key_exists($name, self::$textMapPropagators)) { + if (Sdk::useSpiRegistry()) { + $propagators = self::getFactoryInstances(TextMapPropagatorInterface::class); + } else { + $propagators = self::$textMapPropagators; + } + if (!array_key_exists($name, $propagators)) { throw new RuntimeException('Text map propagator not registered for: ' . $name); } - return self::$textMapPropagators[$name]; + return $propagators[$name]; } public static function resourceDetector(string $name): ResourceDetectorInterface { - self::maybeInitFromSpi(); - if (!array_key_exists($name, self::$resourceDetectors)) { + if (Sdk::useSpiRegistry()) { + $detectors = self::getFactoryInstances(ResourceDetectorFactoryInterface::class); + } else { + $detectors = self::$resourceDetectors; + } + if (!array_key_exists($name, $detectors)) { throw new RuntimeException('Resource detector not registered for: ' . $name); } - return self::$resourceDetectors[$name]; + return $detectors[$name]; } /** @@ -303,8 +308,12 @@ public static function resourceDetector(string $name): ResourceDetectorInterface */ public static function resourceDetectors(): array { - self::maybeInitFromSpi(); + if (Sdk::useSpiRegistry()) { + $detectors = self::getFactoryInstances(ResourceDetectorFactoryInterface::class); + } else { + $detectors = self::$resourceDetectors; + } - return array_values(self::$resourceDetectors); + return array_values($detectors); } } diff --git a/tests/Unit/SDK/FactoryRegistryTest.php b/tests/Unit/SDK/FactoryRegistryTest.php index 79c326c74..59632a12d 100644 --- a/tests/Unit/SDK/FactoryRegistryTest.php +++ b/tests/Unit/SDK/FactoryRegistryTest.php @@ -4,13 +4,17 @@ namespace OpenTelemetry\Tests\Unit\SDK; +use Nevay\SPI\ServiceLoader; use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface; +use OpenTelemetry\SDK\Common\Attribute\Attributes; use OpenTelemetry\SDK\Common\Configuration\Variables; use OpenTelemetry\SDK\Common\Export\TransportFactoryInterface; use OpenTelemetry\SDK\Logs\LogRecordExporterFactoryInterface; use OpenTelemetry\SDK\Metrics\MetricExporterFactoryInterface; use OpenTelemetry\SDK\Registry; +use OpenTelemetry\SDK\Resource\ResourceDetectorFactoryInterface; use OpenTelemetry\SDK\Resource\ResourceDetectorInterface; +use OpenTelemetry\SDK\Resource\ResourceInfo; use OpenTelemetry\SDK\Trace\SpanExporter\SpanExporterFactoryInterface; use OpenTelemetry\Tests\TestState; use PHPUnit\Framework\Attributes\CoversClass; @@ -147,10 +151,44 @@ public static function invalidFactoryProvider(): array ]; } - public function test_retrieve_from_composer_extra(): void + public function test_retrieve_from_spi(): void { $this->setEnvironmentVariable(Variables::OTEL_PHP_EXPERIMENTAL_SPI_REGISTRY, 'true'); $this->assertFileExists(dirname(__DIR__, 3) . '/vendor/composer/GeneratedServiceProviderData.php'); $this->assertInstanceOf(ResourceDetectorInterface::class, Registry::resourceDetector('test')); } + + public function test_add_to_spi(): void + { + $this->setEnvironmentVariable(Variables::OTEL_PHP_EXPERIMENTAL_SPI_REGISTRY, 'true'); + $factory = new class() implements ResourceDetectorFactoryInterface { + public function create(): ResourceDetectorInterface + { + return new class() implements ResourceDetectorInterface { + public function getResource(): ResourceInfo + { + $attributes = [ + 'foo-resource' => 'test-value', + ]; + + return ResourceInfo::create(Attributes::create(['foo-resource' => 'foo'])); + } + }; + } + public function type(): string + { + return 'foo'; + } + public function priority(): int + { + return 99; + } + }; + ServiceLoader::register(ResourceDetectorFactoryInterface::class, $factory::class); + + $detector = Registry::resourceDetector('foo'); + $this->assertInstanceOf(ResourceDetectorInterface::class, $detector); + $this->assertTrue($detector->getResource()->getAttributes()->has('foo-resource')); + $this->assertSame('foo', $detector->getResource()->getAttributes()->get('foo-resource')); + } }