Skip to content

Commit

Permalink
don't static init registry from SPI. add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
brettmc committed Oct 23, 2024
1 parent 8e17eaa commit d252d31
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 42 deletions.
91 changes: 50 additions & 41 deletions src/SDK/Registry.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -24,8 +23,6 @@
*/
class Registry
{
private static bool $initialized = false;

/** @var array<string, SpanExporterFactoryInterface> $spanExporterFactories */
private static array $spanExporterFactories = [];
/** @var array<string, TransportFactoryInterface> $transportFactories */
Expand All @@ -40,27 +37,11 @@ class Registry
/** @var array<string, ResourceDetectorInterface> $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(
Expand All @@ -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(
Expand Down Expand Up @@ -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);

Expand All @@ -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);

Expand All @@ -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);

Expand All @@ -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);

Expand All @@ -280,31 +277,43 @@ 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];
}

/**
* @return array<int, 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);
}
}
40 changes: 39 additions & 1 deletion tests/Unit/SDK/FactoryRegistryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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'));
}
}

0 comments on commit d252d31

Please sign in to comment.