Skip to content

Commit

Permalink
use SPI to manage components (#1412)
Browse files Browse the repository at this point in the history
To resolve our long-standing race conditions stemming from using composer's autoload->files
to registry SDK components at runtime, this changes things so that:
- components are registerd in various _register.php files, and SPI is used to remove those files from autoload->files
- the SDK Registry is removed, replaced by an internal service loader which interacts with SPI similarly to how the Registry did
  • Loading branch information
brettmc authored Jan 29, 2025
1 parent 27dfc42 commit 13c2b93
Show file tree
Hide file tree
Showing 106 changed files with 1,192 additions and 604 deletions.
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ update: ## Update dependencies
$(DC_RUN_PHP) env XDEBUG_MODE=off composer update
update-lowest: ## Update dependencies to lowest supported versions
$(DC_RUN_PHP) env XDEBUG_MODE=off composer update --prefer-lowest
dump: ## Dump autoload
$(DC_RUN_PHP) env XDEBUG_MODE=off composer dump-autoload
test: test-unit test-integration ## Run unit and integration tests
test-unit: ## Run unit tests
$(DC_RUN_PHP) env XDEBUG_MODE=coverage vendor/bin/phpunit --testsuite unit --colors=always
Expand Down
75 changes: 21 additions & 54 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@
"open-telemetry/exporter-otlp": "1.0.x-dev",
"open-telemetry/exporter-zipkin": "1.0.x-dev",
"open-telemetry/extension-propagator-b3": "1.0.x-dev",
"open-telemetry/extension-propagator-jaeger": "0.0.2",
"open-telemetry/extension-propagator-cloudtrace": "0.0.4",
"open-telemetry/extension-propagator-jaeger": "0.0.3",
"open-telemetry/gen-otlp-protobuf": "1.0.x-dev",
"open-telemetry/sdk": "1.0.x-dev",
"open-telemetry/sdk-configuration": "0.1.x-dev",
Expand All @@ -59,17 +60,13 @@
},
"files": [
"src/Context/fiber/initialize_fiber_handler.php",
"src/API/_register.php",
"src/API/Trace/functions.php",
"src/Config/SDK/_register.php",
"src/Contrib/Otlp/_register.php",
"src/Contrib/Grpc/_register.php",
"src/Contrib/Zipkin/_register.php",
"src/Extension/Propagator/B3/_register.php",
"src/Extension/Propagator/CloudTrace/_register.php",
"src/Extension/Propagator/Jaeger/_register.php",
"src/SDK/Logs/Exporter/_register.php",
"src/SDK/Metrics/MetricExporter/_register.php",
"src/SDK/Propagation/_register.php",
"src/SDK/Trace/SpanExporter/_register.php",
"src/SDK/_register.php",
"src/SDK/Common/Dev/Compatibility/_load.php",
"src/SDK/Common/Util/functions.php",
"src/SDK/_autoload.php"
Expand All @@ -80,7 +77,11 @@
"OpenTelemetry\\Tests\\": "tests/",
"OpenTelemetry\\Example\\": "examples/src/",
"ExampleSDK\\ComponentProvider\\": "tests/Unit/Config/SDK/Configuration/ExampleSdk/"
}
},
"files": [
"examples/src/_register.php",
"tests/_register.php"
]
},
"require-dev": {
"ext-grpc": "*",
Expand Down Expand Up @@ -123,52 +124,18 @@
"target-directory": "vendor-bin",
"forward-command": true
},
"spi": {
"OpenTelemetry\\Config\\SDK\\Configuration\\ComponentProvider": [
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Propagator\\TextMapPropagatorB3",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Propagator\\TextMapPropagatorB3Multi",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Propagator\\TextMapPropagatorBaggage",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Propagator\\TextMapPropagatorComposite",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Propagator\\TextMapPropagatorJaeger",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Propagator\\TextMapPropagatorTraceContext",

"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SamplerAlwaysOff",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SamplerAlwaysOn",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SamplerParentBased",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SamplerTraceIdRatioBased",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SpanExporterConsole",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SpanExporterOtlp",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SpanExporterZipkin",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SpanProcessorBatch",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SpanProcessorSimple",

"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Metrics\\AggregationResolverDefault",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Metrics\\MetricExporterConsole",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Metrics\\MetricExporterOtlp",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Metrics\\MetricReaderPeriodic",

"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordExporterConsole",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordExporterOtlp",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordProcessorBatch",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordProcessorSimple",

"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Instrumentation\\General\\HttpConfigProvider",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Instrumentation\\General\\PeerConfigProvider",

"OpenTelemetry\\Example\\ExampleConfigProvider",

"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\\TextMapPropagatorXray",
"OpenTelemetry\\Tests\\Integration\\Config\\ComponentProvider\\Propagator\\TextMapPropagatorOtTrace"
],
"OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\HookManagerInterface": [
"OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\ExtensionHookManager"
"spi-config": {
"autoload-files": [
"examples/src/_register.php",
"src/API/_register.php",
"src/Config/SDK/_register.php",
"src/Contrib/Otlp/_register.php",
"src/Contrib/Grpc/_register.php",
"src/Contrib/Zipkin/_register.php",
"src/SDK/_register.php",
"tests/_register.php"
],
"OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\Instrumentation": [
"OpenTelemetry\\Example\\ExampleInstrumentation"
]
"prune-autoload-files": true
}
}
}
6 changes: 0 additions & 6 deletions deptrac.baseline.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,2 @@
deptrac:
skip_violations:
/src/Extension/Propagator/B3/_register.php:
- OpenTelemetry\SDK\Registry
/src/Extension/Propagator/CloudTrace/_register.php:
- OpenTelemetry\SDK\Registry
/src/Extension/Propagator/Jaeger/_register.php:
- OpenTelemetry\SDK\Registry
1 change: 1 addition & 0 deletions deptrac.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ deptrac:
SDK:
- +API
- ConfigSDK
- Extension
- SemConv
- PsrHttp
- HttpPlug
Expand Down
27 changes: 27 additions & 0 deletions docs/upgrading.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Upgrading notes for major versions

## 1.x -> 2.x

### SDK

#### SDK\Registry removed
`SDK\Registry` has been removed, and the technique of registering components (eg propagators, transports,
auto-instrumentations) has been replaced with [Nevay/SPI](https://github.com/Nevay/spi/) ServiceLoader.

The ServiceLoader should be configured through existing `_register.php` files, which should *only* contain calls to
SPI's `ServiceLoader::register()` method.

SPI has a composer plugin which will scan for `ServiceLoader::register()` calls in `autoload.files` and generate a
`GeneratedServiceProviderData.php` file in `vendor/composer/`. The plugin will then remove those `autoload.files` entries
from composer's generated output to avoid double-loading.
Pre-generating the services in this way avoids a race-condition in 1.x where composer's `autoload.files` are executed in an
undefined order, and services may not be registered in time for the SDK to use them.

For SPI to work correctly, the composer plugin _should_ be allowed to run. If the plugin is not allowed to run, then
services will still register at runtime, however this might still suffer from the same race-condition as `1.x`.

#### FactoryInterfaces updated
Various component factory interfaces (eg `TextMapPropagatorFactoryInterface`, `TransportFactoryInterface`) have been
updated to include `priority()` and `type()` methods. These are used in conjunction with SPI ServiceLoader to associate
a type (eg `otlp`) with a factory, and to allow SDK-provided factories to be replaced by user-provided factories (by
providing a higher priority for the same type).
15 changes: 14 additions & 1 deletion examples/autoload_sdk_with_custom_transport.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

namespace OpenTelemetry\Example;

use Nevay\SPI\ServiceLoader;
use OpenTelemetry\SDK\Common\Export\TransportFactoryInterface;

putenv('OTEL_PHP_AUTOLOAD_ENABLED=true');
putenv('OTEL_TRACES_EXPORTER=otlp');
putenv('OTEL_EXPORTER_OTLP_PROTOCOL=grpc');
Expand Down Expand Up @@ -45,9 +48,19 @@ public function forceFlush(?\OpenTelemetry\SDK\Common\Future\CancellationInterfa
}
};
}

public function type(): string
{
return 'grpc';
}

public function priority(): int
{
return 100;
}
};

\OpenTelemetry\SDK\Registry::registerTransportFactory('grpc', $factory, true);
ServiceLoader::register(TransportFactoryInterface::class, $factory::class);

$instrumentation = new \OpenTelemetry\API\Instrumentation\CachedInstrumentation('demo');

Expand Down
4 changes: 2 additions & 2 deletions examples/logs/exporters/otlp_http.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use OpenTelemetry\API\Logs\LogRecord;
use OpenTelemetry\API\Logs\Severity;
use OpenTelemetry\Contrib\Otlp\LogsExporter;
use OpenTelemetry\Contrib\Otlp\OtlpHttpTransportFactory;
use OpenTelemetry\SDK\Common\Export\Http\PsrTransportFactory;
use OpenTelemetry\SDK\Common\Instrumentation\InstrumentationScopeFactory;
use OpenTelemetry\SDK\Logs\EventLoggerProvider;
use OpenTelemetry\SDK\Logs\LoggerProvider;
Expand All @@ -16,7 +16,7 @@

require __DIR__ . '/../../../vendor/autoload.php';

$transport = (new OtlpHttpTransportFactory())->create('http://collector:4318/v1/logs', 'application/json');
$transport = (new PsrTransportFactory())->create('http://collector:4318/v1/logs', 'application/json');
$exporter = new LogsExporter($transport);

$loggerProvider = new LoggerProvider(
Expand Down
5 changes: 5 additions & 0 deletions examples/src/ExampleConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,9 @@ public function __construct(
public readonly bool $enabled = true,
) {
}

public static function default(): self
{
return new self('example');
}
}
3 changes: 1 addition & 2 deletions examples/src/ExampleInstrumentation.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

namespace OpenTelemetry\Example;

use Exception;
use OpenTelemetry\API\Configuration\ConfigProperties;
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\Context as InstrumentationContext;
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\HookManagerInterface;
Expand All @@ -16,7 +15,7 @@ final class ExampleInstrumentation implements Instrumentation
{
public function register(HookManagerInterface $hookManager, ConfigProperties $configuration, InstrumentationContext $context): void
{
$config = $configuration->get(ExampleConfig::class) ?? throw new Exception('example instrumentation must be configured');
$config = $configuration->get(ExampleConfig::class) ?? ExampleConfig::default();
if (!$config->enabled) {
return;
}
Expand Down
22 changes: 22 additions & 0 deletions examples/src/TestResourceDetector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\Example;

use OpenTelemetry\SDK\Common\Attribute\Attributes;
use OpenTelemetry\SDK\Resource\ResourceDetectorInterface;
use OpenTelemetry\SDK\Resource\ResourceInfo;
use OpenTelemetry\SemConv\ResourceAttributes;

class TestResourceDetector implements ResourceDetectorInterface
{
public function getResource(): ResourceInfo
{
$attributes = [
'test-resource' => 'test-value',
];

return ResourceInfo::create(Attributes::create($attributes), ResourceAttributes::SCHEMA_URL);
}
}
26 changes: 26 additions & 0 deletions examples/src/TestResourceDetectorFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\Example;

use OpenTelemetry\SDK\Resource\ResourceDetectorFactoryInterface;
use OpenTelemetry\SDK\Resource\ResourceDetectorInterface;

class TestResourceDetectorFactory implements ResourceDetectorFactoryInterface
{
public function create(): ResourceDetectorInterface
{
return new TestResourceDetector();
}

public function type(): string
{
return 'test';
}

public function priority(): int
{
return 0;
}
}
15 changes: 15 additions & 0 deletions examples/src/_register.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

use Nevay\SPI\ServiceLoader;
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\Instrumentation;
use OpenTelemetry\Config\SDK\Configuration\ComponentProvider;
use OpenTelemetry\Example\ExampleConfigProvider;
use OpenTelemetry\Example\ExampleInstrumentation;
use OpenTelemetry\Example\TestResourceDetectorFactory;
use OpenTelemetry\SDK\Resource\ResourceDetectorFactoryInterface;

ServiceLoader::register(Instrumentation::class, ExampleInstrumentation::class);
ServiceLoader::register(ResourceDetectorFactoryInterface::class, TestResourceDetectorFactory::class);
ServiceLoader::register(ComponentProvider::class, ExampleConfigProvider::class);
4 changes: 2 additions & 2 deletions examples/traces/exporters/otlp_http.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@

require __DIR__ . '/../../../vendor/autoload.php';

use OpenTelemetry\Contrib\Otlp\OtlpHttpTransportFactory;
use OpenTelemetry\Contrib\Otlp\SpanExporter;
use OpenTelemetry\SDK\Common\Export\Http\PsrTransportFactory;
use OpenTelemetry\SDK\Trace\SpanProcessor\SimpleSpanProcessor;
use OpenTelemetry\SDK\Trace\TracerProvider;

$transport = (new OtlpHttpTransportFactory())->create('http://collector:4318/v1/traces', 'application/x-protobuf');
$transport = (new PsrTransportFactory())->create('http://collector:4318/v1/traces', 'application/x-protobuf');
$exporter = new SpanExporter($transport);

echo 'Starting OTLP example';
Expand Down
4 changes: 2 additions & 2 deletions examples/traces/exporters/otlp_http_json.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
require __DIR__ . '/../../../vendor/autoload.php';

use OpenTelemetry\Contrib\Otlp\ContentTypes;
use OpenTelemetry\Contrib\Otlp\OtlpHttpTransportFactory;
use OpenTelemetry\Contrib\Otlp\SpanExporter;
use OpenTelemetry\SDK\Common\Export\Http\PsrTransportFactory;
use OpenTelemetry\SDK\Trace\SpanProcessor\SimpleSpanProcessor;
use OpenTelemetry\SDK\Trace\TracerProvider;

$transport = (new OtlpHttpTransportFactory())->create('http://collector:4318/v1/traces', ContentTypes::JSON);
$transport = (new PsrTransportFactory())->create('http://collector:4318/v1/traces', ContentTypes::JSON);
$exporter = new SpanExporter($transport);

echo 'Starting OTLP+json example';
Expand Down
3 changes: 3 additions & 0 deletions examples/traces/features/auto_root_span.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@
putenv('OTEL_TRACES_EXPORTER=console');
putenv('OTEL_METRICS_EXPORTER=none');
putenv('OTEL_LOGS_EXPORTER=console');
putenv('OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4318');
putenv('OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf');
putenv('OTEL_PROPAGATORS=tracecontext');
putenv('OTEL_PHP_EXPERIMENTAL_AUTO_ROOT_SPAN=true');
putenv('OTEL_PHP_DETECTORS=sdk,test');

//Usage: php -S localhost:8080 examples/traces/features/auto_root_span.php

Expand Down
2 changes: 1 addition & 1 deletion phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,4 @@ parameters:
-
message: "#.*return with type T is not subtype.*#"
paths:
- src/SDK/Common/InstrumentationScope
- src/SDK/Common/InstrumentationScope
9 changes: 9 additions & 0 deletions src/API/_register.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

use Nevay\SPI\ServiceLoader;
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\ExtensionHookManager;
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\HookManagerInterface;

ServiceLoader::register(HookManagerInterface::class, ExtensionHookManager::class);
13 changes: 8 additions & 5 deletions src/API/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
"require": {
"php": "^8.2",
"open-telemetry/context": "^1.0|^2.0",
"psr/log": "^1.1|^2.0|^3.0"
"psr/log": "^1.1|^2.0|^3.0",
"tbachert/spi": "^1.0.1"
},
"conflict": {
"open-telemetry/sdk": "<=1.0.8"
Expand All @@ -29,6 +30,7 @@
"OpenTelemetry\\API\\": "."
},
"files": [
"_register.php",
"Trace/functions.php"
]
},
Expand All @@ -37,10 +39,11 @@
"dev-main": "1.1.x-dev",
"dev-2.x": "2.x-dev"
},
"spi": {
"OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\HookManagerInterface": [
"OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\ExtensionHookManager"
]
"spi-config": {
"autoload-files": [
"_register.php"
],
"prune-autoload-files": true
}
}
}
Loading

0 comments on commit 13c2b93

Please sign in to comment.