diff --git a/examples/sdk_builder.php b/examples/sdk_builder.php new file mode 100644 index 000000000..037c39cad --- /dev/null +++ b/examples/sdk_builder.php @@ -0,0 +1,79 @@ +create('http://collector:4318/v1/metrics', 'application/x-protobuf') + ), + ClockFactory::getDefault() +); + +$meterProvider = MeterProvider::builder() + ->setResource($resource) + ->addReader($reader) + ->build(); + +$tracerProvider = TracerProvider::builder() + ->addSpanProcessor( + (new BatchSpanProcessorBuilder($spanExporter)) + ->setMeterProvider($meterProvider) + ->build() + ) + ->setResource($resource) + ->setSampler(new ParentBased(new AlwaysOnSampler())) + ->build(); + +Sdk::builder() + ->setTracerProvider($tracerProvider) + ->setMeterProvider($meterProvider) + ->setPropagator(TraceContextPropagator::getInstance()) + ->setAutoShutdown(true) + ->buildAndRegisterGlobal(); + +$instrumentation = new CachedInstrumentation('example'); +$tracer = $instrumentation->tracer(); + +$root = $tracer->spanBuilder('root')->startSpan(); +$scope = $root->activate(); +for ($i=0; $i < 100; $i++) { + if ($i%8 === 0) { + $reader->collect(); + } + $tracer->spanBuilder('span-' . $i) + ->startSpan() + ->end(); + usleep(50000); +} +$scope->detach(); +$root->end(); +$reader->shutdown(); + +echo 'Finished SDK builder example' . PHP_EOL; diff --git a/src/SDK/Metrics/MeterProvider.php b/src/SDK/Metrics/MeterProvider.php index 6352d701c..572069ec2 100644 --- a/src/SDK/Metrics/MeterProvider.php +++ b/src/SDK/Metrics/MeterProvider.php @@ -13,7 +13,7 @@ use OpenTelemetry\SDK\Metrics\Exemplar\ExemplarFilterInterface; use OpenTelemetry\SDK\Metrics\MetricFactory\StreamFactory; use OpenTelemetry\SDK\Resource\ResourceInfo; -use OpenTelemetry\SDK\SDK; +use OpenTelemetry\SDK\Sdk; final class MeterProvider implements MeterProviderInterface { @@ -65,7 +65,7 @@ public function getMeter( ?string $schemaUrl = null, iterable $attributes = [] ): MeterInterface { - if ($this->closed || SDK::isDisabled()) { + if ($this->closed || Sdk::isDisabled()) { //@todo create meter provider from factory, and move Sdk::isDisabled() there return new NoopMeter(); } @@ -117,4 +117,9 @@ public function forceFlush(): bool return $success; } + + public static function builder(): MeterProviderBuilder + { + return new MeterProviderBuilder(); + } } diff --git a/src/SDK/Metrics/MeterProviderBuilder.php b/src/SDK/Metrics/MeterProviderBuilder.php new file mode 100644 index 000000000..e05750b56 --- /dev/null +++ b/src/SDK/Metrics/MeterProviderBuilder.php @@ -0,0 +1,60 @@ + + private array $metricReaders = []; + private ?ResourceInfo $resource = null; + + public function registerMetricReader(MetricReaderInterface $reader): self + { + $this->metricReaders[] = $reader; + + return $this; + } + + public function setResource(ResourceInfo $resource): self + { + $this->resource = $resource; + + return $this; + } + + public function addReader(MetricReaderInterface $reader): self + { + $this->metricReaders[] = $reader; + + return $this; + } + + /** + * @psalm-suppress PossiblyInvalidArgument + */ + public function build(): MeterProviderInterface + { + return new MeterProvider( + null, + $this->resource ?? ResourceInfoFactory::emptyResource(), + ClockFactory::getDefault(), + Attributes::factory(), + new InstrumentationScopeFactory(Attributes::factory()), + $this->metricReaders, + new CriteriaViewRegistry(), + new WithSampledTraceExemplarFilter(), + new ImmediateStalenessHandlerFactory(), + ); + } +} diff --git a/src/SDK/Metrics/NoopMeterProvider.php b/src/SDK/Metrics/NoopMeterProvider.php new file mode 100644 index 000000000..2efb484d3 --- /dev/null +++ b/src/SDK/Metrics/NoopMeterProvider.php @@ -0,0 +1,26 @@ +tracerProvider = $tracerProvider; + $this->meterProvider = $meterProvider; + $this->propagator = $propagator; + } + + public static function isDisabled(): bool + { + return EnvironmentVariables::getBoolean(Variables::OTEL_SDK_DISABLED); + } + + public static function builder(): SdkBuilder + { + return new SdkBuilder(); + } + + public function getTracerProvider(): TracerProviderInterface + { + return $this->tracerProvider; + } + + public function getMeterProvider(): MeterProviderInterface + { + return $this->meterProvider; + } + + public function getPropagator(): TextMapPropagatorInterface + { + return $this->propagator; + } +} diff --git a/src/SDK/SdkBuilder.php b/src/SDK/SdkBuilder.php new file mode 100644 index 000000000..301a3e158 --- /dev/null +++ b/src/SDK/SdkBuilder.php @@ -0,0 +1,84 @@ +autoShutdown = $shutdown; + + return $this; + } + + public function setTracerProvider(TracerProviderInterface $provider): self + { + $this->tracerProvider = $provider; + + return $this; + } + + public function setMeterProvider(MeterProviderInterface $meterProvider): self + { + $this->meterProvider = $meterProvider; + + return $this; + } + + public function setPropagator(TextMapPropagatorInterface $propagator): self + { + $this->propagator = $propagator; + + return $this; + } + + public function build(): Sdk + { + $tracerProvider = $this->tracerProvider ?? new NoopTracerProvider(); + $meterProvider = $this->meterProvider ?? new NoopMeterProvider(); + if ($this->autoShutdown) { + ShutdownHandler::register(fn (?CancellationInterface $cancellation = null): bool => $tracerProvider->shutdown($cancellation)); + ShutdownHandler::register(fn (): bool => $meterProvider->shutdown()); + } + + return new Sdk( + $tracerProvider, + $meterProvider, + $this->propagator ?? NoopTextMapPropagator::getInstance(), + ); + } + + public function buildAndRegisterGlobal(): ScopeInterface + { + $sdk = $this->build(); + $context = Configurator::create() + ->withPropagator($sdk->getPropagator()) + ->withTracerProvider($sdk->getTracerProvider()) + ->withMeterProvider($sdk->getMeterProvider()) + ->storeInContext(); + + return Context::storage()->attach($context); + } +} diff --git a/src/SDK/Trace/SpanProcessor/BatchSpanProcessorBuilder.php b/src/SDK/Trace/SpanProcessor/BatchSpanProcessorBuilder.php new file mode 100644 index 000000000..8e81e7dd6 --- /dev/null +++ b/src/SDK/Trace/SpanProcessor/BatchSpanProcessorBuilder.php @@ -0,0 +1,41 @@ +exporter = $exporter; + } + + public function setMeterProvider(MeterProviderInterface $meterProvider): self + { + $this->meterProvider = $meterProvider; + + return $this; + } + + public function build(): BatchSpanProcessor + { + return new BatchSpanProcessor( + $this->exporter, + ClockFactory::getDefault(), + BatchSpanProcessor::DEFAULT_MAX_QUEUE_SIZE, + BatchSpanProcessor::DEFAULT_SCHEDULE_DELAY, + BatchSpanProcessor::DEFAULT_EXPORT_TIMEOUT, + BatchSpanProcessor::DEFAULT_MAX_EXPORT_BATCH_SIZE, + true, + $this->meterProvider + ); + } +} diff --git a/src/SDK/Trace/TracerProvider.php b/src/SDK/Trace/TracerProvider.php index eeb3cfdb2..fdae4aea2 100644 --- a/src/SDK/Trace/TracerProvider.php +++ b/src/SDK/Trace/TracerProvider.php @@ -13,7 +13,6 @@ use OpenTelemetry\SDK\Common\Instrumentation\InstrumentationScopeFactoryInterface; use OpenTelemetry\SDK\Resource\ResourceInfo; use OpenTelemetry\SDK\Resource\ResourceInfoFactory; -use OpenTelemetry\SDK\SDK; use OpenTelemetry\SDK\Trace\Sampler\AlwaysOnSampler; use OpenTelemetry\SDK\Trace\Sampler\ParentBased; @@ -66,7 +65,7 @@ public function getTracer( ?string $schemaUrl = null, iterable $attributes = [] ): API\TracerInterface { - if ($this->tracerSharedState->hasShutdown() || SDK::isDisabled()) { + if ($this->tracerSharedState->hasShutdown()) { return NoopTracer::getInstance(); } @@ -92,4 +91,9 @@ public function shutdown(?CancellationInterface $cancellation = null): bool return $this->tracerSharedState->shutdown($cancellation); } + + public static function builder(): TracerProviderBuilder + { + return new TracerProviderBuilder(); + } } diff --git a/src/SDK/Trace/TracerProviderBuilder.php b/src/SDK/Trace/TracerProviderBuilder.php new file mode 100644 index 000000000..8dcfdc700 --- /dev/null +++ b/src/SDK/Trace/TracerProviderBuilder.php @@ -0,0 +1,45 @@ + + private ?array $spanProcessors = []; + private ?ResourceInfo $resource = null; + private ?SamplerInterface $sampler = null; + + public function addSpanProcessor(SpanProcessorInterface $spanProcessor): self + { + $this->spanProcessors[] = $spanProcessor; + + return $this; + } + + public function setResource(ResourceInfo $resource): self + { + $this->resource = $resource; + + return $this; + } + + public function setSampler(SamplerInterface $sampler): self + { + $this->sampler = $sampler; + + return $this; + } + + public function build(): TracerProviderInterface + { + return new TracerProvider( + $this->spanProcessors, + $this->sampler, + $this->resource, + ); + } +} diff --git a/src/SDK/Trace/TracerProviderFactory.php b/src/SDK/Trace/TracerProviderFactory.php index 55be3dde2..49e7c81cd 100644 --- a/src/SDK/Trace/TracerProviderFactory.php +++ b/src/SDK/Trace/TracerProviderFactory.php @@ -5,7 +5,7 @@ namespace OpenTelemetry\SDK\Trace; use OpenTelemetry\SDK\Behavior\LogsMessagesTrait; -use OpenTelemetry\SDK\SDK; +use OpenTelemetry\SDK\Sdk; final class TracerProviderFactory { @@ -28,7 +28,7 @@ public function __construct( public function create(): TracerProviderInterface { - if (SDK::isDisabled()) { + if (Sdk::isDisabled()) { return new NoopTracerProvider(); } diff --git a/tests/Integration/SDK/TracerTest.php b/tests/Integration/SDK/TracerTest.php index 39b1e2daa..606fcd408 100644 --- a/tests/Integration/SDK/TracerTest.php +++ b/tests/Integration/SDK/TracerTest.php @@ -20,6 +20,7 @@ use OpenTelemetry\SDK\Trace\SpanProcessor\SimpleSpanProcessor; use OpenTelemetry\SDK\Trace\SpanProcessorInterface; use OpenTelemetry\SDK\Trace\TracerProvider; +use OpenTelemetry\SDK\Trace\TracerProviderFactory; use OpenTelemetry\SemConv\TraceAttributes; use PHPUnit\Framework\TestCase; @@ -107,10 +108,10 @@ public function test_returns_noop_span_builder_after_shutdown(): void $this->assertInstanceOf(API\NoopSpanBuilder::class, $tracer->spanBuilder('baz')); } - public function test_returns_noop_tracer_when_sdk_disabled(): void + public function test_factory_returns_noop_tracer_when_sdk_disabled(): void { self::setEnvironmentVariable('OTEL_SDK_DISABLED', 'true'); - $tracerProvider = new TracerProvider(); + $tracerProvider = (new TracerProviderFactory('test'))->create(); $tracer = $tracerProvider->getTracer('foo'); $this->assertInstanceOf(API\NoopTracer::class, $tracer); } diff --git a/tests/Unit/SDK/Resource/Detectors/SdkTest.php b/tests/Unit/SDK/Resource/Detectors/SdkTest.php index 0cb9f6f96..6f09ce705 100644 --- a/tests/Unit/SDK/Resource/Detectors/SdkTest.php +++ b/tests/Unit/SDK/Resource/Detectors/SdkTest.php @@ -18,7 +18,7 @@ public function test_sdk_get_resource(): void { $resouceDetector = new Detectors\Sdk(); $resource = $resouceDetector->getResource(); - $version = InstalledVersions::getVersion('open-telemetry/opentelemetry'); + $version = InstalledVersions::getPrettyVersion('open-telemetry/opentelemetry'); $this->assertSame(ResourceAttributes::SCHEMA_URL, $resource->getSchemaUrl()); $this->assertSame('opentelemetry', $resource->getAttributes()->get(ResourceAttributes::TELEMETRY_SDK_NAME)); diff --git a/tests/Unit/SDK/Resource/ResourceInfoTest.php b/tests/Unit/SDK/Resource/ResourceInfoTest.php index a1dd98b4f..881e1ff02 100644 --- a/tests/Unit/SDK/Resource/ResourceInfoTest.php +++ b/tests/Unit/SDK/Resource/ResourceInfoTest.php @@ -35,7 +35,7 @@ public function test_get_attributes(): void new Detectors\SdkProvided(), ]))->getResource(); - $version = InstalledVersions::getVersion('open-telemetry/opentelemetry'); + $version = InstalledVersions::getPrettyVersion('open-telemetry/opentelemetry'); $name = $resource->getAttributes()->get('name'); $sdkname = $resource->getAttributes()->get(ResourceAttributes::TELEMETRY_SDK_NAME); diff --git a/tests/Unit/SDK/SdkBuilderTest.php b/tests/Unit/SDK/SdkBuilderTest.php new file mode 100644 index 000000000..58d9960a7 --- /dev/null +++ b/tests/Unit/SDK/SdkBuilderTest.php @@ -0,0 +1,51 @@ +propagator = $this->createMock(TextMapPropagatorInterface::class); + $this->tracerProvider = $this->createMock(TracerProviderInterface::class); + $this->meterProvider = $this->createMock(MeterProviderInterface::class); + $this->builder = (new SdkBuilder()) + ->setMeterProvider($this->meterProvider) + ->setPropagator($this->propagator) + ->setTracerProvider($this->tracerProvider); + } + + public function test_build(): void + { + $sdk = $this->builder->build(); + $this->assertSame($this->meterProvider, $sdk->getMeterProvider()); + $this->assertSame($this->propagator, $sdk->getPropagator()); + $this->assertSame($this->tracerProvider, $sdk->getTracerProvider()); + } + + public function test_build_and_register_global(): void + { + $scope = $this->builder->buildAndRegisterGlobal(); + $this->assertSame($this->meterProvider, Globals::meterProvider()); + $this->assertSame($this->propagator, Globals::propagator()); + $this->assertSame($this->tracerProvider, Globals::tracerProvider()); + $scope->detach(); + } +} diff --git a/tests/Unit/SDK/SdkTest.php b/tests/Unit/SDK/SdkTest.php index 23c2c9081..69047f31c 100644 --- a/tests/Unit/SDK/SdkTest.php +++ b/tests/Unit/SDK/SdkTest.php @@ -5,11 +5,15 @@ namespace OpenTelemetry\Tests\Unit\SDK; use AssertWell\PHPUnitGlobalState\EnvironmentVariables; -use OpenTelemetry\SDK\SDK; +use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface; +use OpenTelemetry\SDK\Metrics\MeterProviderInterface; +use OpenTelemetry\SDK\Sdk; +use OpenTelemetry\SDK\SdkBuilder; +use OpenTelemetry\SDK\Trace\TracerProviderInterface; use PHPUnit\Framework\TestCase; /** - * @covers \OpenTelemetry\SDK\SDK + * @covers \OpenTelemetry\SDK\Sdk */ class SdkTest extends TestCase { @@ -22,7 +26,7 @@ public function tearDown(): void public function test_is_not_disabled_by_default(): void { - $this->assertFalse(SDK::isDisabled()); + $this->assertFalse(Sdk::isDisabled()); } /** @@ -31,7 +35,7 @@ public function test_is_not_disabled_by_default(): void public function test_is_disabled(string $value, bool $expected): void { self::setEnvironmentVariable('OTEL_SDK_DISABLED', $value); - $this->assertSame($expected, SDK::isDisabled()); + $this->assertSame($expected, Sdk::isDisabled()); } public function disabledProvider(): array { @@ -42,4 +46,20 @@ public function disabledProvider(): array ['0', false], ]; } + + public function test_builder(): void + { + $this->assertInstanceOf(SdkBuilder::class, Sdk::builder()); + } + + public function test_getters(): void + { + $propagator = $this->createMock(TextMapPropagatorInterface::class); + $meterProvider = $this->createMock(MeterProviderInterface::class); + $tracerProvider = $this->createMock(TracerProviderInterface::class); + $sdk = new Sdk($tracerProvider, $meterProvider, $propagator); + $this->assertSame($propagator, $sdk->getPropagator()); + $this->assertSame($meterProvider, $sdk->getMeterProvider()); + $this->assertSame($tracerProvider, $sdk->getTracerProvider()); + } }