Sdk builder (#845)

Adding an SDK builder and example usage.
This commit is contained in:
Brett McBride 2022-10-31 08:09:01 +11:00 committed by GitHub
parent 87989731ed
commit ba9899278f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 483 additions and 30 deletions

79
examples/sdk_builder.php Normal file
View File

@ -0,0 +1,79 @@
<?php
declare(strict_types=1);
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\HttpFactory;
use OpenTelemetry\API\Common\Instrumentation\CachedInstrumentation;
use OpenTelemetry\API\Trace\Propagation\TraceContextPropagator;
use OpenTelemetry\Contrib\Otlp\MetricExporter;
use OpenTelemetry\SDK\Common\Export\Http\PsrTransportFactory;
use OpenTelemetry\SDK\Common\Time\ClockFactory;
use OpenTelemetry\SDK\Metrics\MeterProvider;
use OpenTelemetry\SDK\Metrics\MetricReader\ExportingReader;
use OpenTelemetry\SDK\Resource\ResourceInfoFactory;
use OpenTelemetry\SDK\Sdk;
use OpenTelemetry\SDK\Trace\Sampler\AlwaysOnSampler;
use OpenTelemetry\SDK\Trace\Sampler\ParentBased;
use OpenTelemetry\SDK\Trace\SpanExporter\InMemoryExporter;
use OpenTelemetry\SDK\Trace\SpanProcessor\BatchSpanProcessorBuilder;
use OpenTelemetry\SDK\Trace\TracerProvider;
require __DIR__ . '/../vendor/autoload.php';
echo 'Starting SDK builder example' . PHP_EOL;
\OpenTelemetry\SDK\Common\Log\LoggerHolder::set(new \Monolog\Logger('grpc', [new \Monolog\Handler\StreamHandler('php://stderr')]));
$resource = ResourceInfoFactory::defaultResource();
$spanExporter = new InMemoryExporter();
$reader = new ExportingReader(
new MetricExporter(
(new PsrTransportFactory(new Client(), new HttpFactory(), new HttpFactory()))
->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;

View File

@ -13,7 +13,7 @@ use OpenTelemetry\SDK\Common\Time\ClockInterface;
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 @@ final class MeterProvider implements MeterProviderInterface
?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 @@ final class MeterProvider implements MeterProviderInterface
return $success;
}
public static function builder(): MeterProviderBuilder
{
return new MeterProviderBuilder();
}
}

View File

@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
namespace OpenTelemetry\SDK\Metrics;
use OpenTelemetry\SDK\Common\Attribute\Attributes;
use OpenTelemetry\SDK\Common\Instrumentation\InstrumentationScopeFactory;
use OpenTelemetry\SDK\Common\Time\ClockFactory;
use OpenTelemetry\SDK\Metrics\Exemplar\ExemplarFilter\WithSampledTraceExemplarFilter;
use OpenTelemetry\SDK\Metrics\StalenessHandler\ImmediateStalenessHandlerFactory;
use OpenTelemetry\SDK\Metrics\View\CriteriaViewRegistry;
use OpenTelemetry\SDK\Resource\ResourceInfo;
use OpenTelemetry\SDK\Resource\ResourceInfoFactory;
class MeterProviderBuilder
{
// @var array<MetricReaderInterface>
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(),
);
}
}

View File

@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace OpenTelemetry\SDK\Metrics;
use OpenTelemetry\API\Metrics\MeterInterface;
use OpenTelemetry\API\Metrics\Noop\NoopMeter;
class NoopMeterProvider implements MeterProviderInterface
{
public function shutdown(): bool
{
return true;
}
public function forceFlush(): bool
{
return true;
}
public function getMeter(string $name, ?string $version = null, ?string $schemaUrl = null, iterable $attributes = []): MeterInterface
{
return new NoopMeter();
}
}

View File

@ -1,16 +0,0 @@
<?php
declare(strict_types=1);
namespace OpenTelemetry\SDK;
use OpenTelemetry\SDK\Common\Environment\EnvironmentVariables;
use OpenTelemetry\SDK\Common\Environment\Variables;
class SDK
{
public static function isDisabled(): bool
{
return EnvironmentVariables::getBoolean(Variables::OTEL_SDK_DISABLED);
}
}

53
src/SDK/Sdk.php Normal file
View File

@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace OpenTelemetry\SDK;
use OpenTelemetry\API\Metrics\MeterProviderInterface;
use OpenTelemetry\API\Trace\TracerProviderInterface;
use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface;
use OpenTelemetry\SDK\Common\Environment\EnvironmentVariables;
use OpenTelemetry\SDK\Common\Environment\Variables;
class Sdk
{
private TracerProviderInterface $tracerProvider;
private MeterProviderInterface $meterProvider;
private TextMapPropagatorInterface $propagator;
public function __construct(
TracerProviderInterface $tracerProvider,
MeterProviderInterface $meterProvider,
TextMapPropagatorInterface $propagator
) {
$this->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;
}
}

84
src/SDK/SdkBuilder.php Normal file
View File

@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
namespace OpenTelemetry\SDK;
use OpenTelemetry\API\Common\Instrumentation\Configurator;
use OpenTelemetry\Context\Context;
use OpenTelemetry\Context\Propagation\NoopTextMapPropagator;
use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface;
use OpenTelemetry\Context\ScopeInterface;
use OpenTelemetry\SDK\Common\Future\CancellationInterface;
use OpenTelemetry\SDK\Common\Util\ShutdownHandler;
use OpenTelemetry\SDK\Metrics\MeterProviderInterface;
use OpenTelemetry\SDK\Metrics\NoopMeterProvider;
use OpenTelemetry\SDK\Trace\NoopTracerProvider;
use OpenTelemetry\SDK\Trace\TracerProviderInterface;
class SdkBuilder
{
private ?TracerProviderInterface $tracerProvider = null;
private ?MeterProviderInterface $meterProvider = null;
private ?TextMapPropagatorInterface $propagator = null;
private bool $autoShutdown = false;
/**
* Automatically shut down providers on process completion. If not set, the user is responsible for calling `shutdown`.
*/
public function setAutoShutdown(bool $shutdown): self
{
$this->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);
}
}

View File

@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace OpenTelemetry\SDK\Trace\SpanProcessor;
use OpenTelemetry\SDK\Common\Time\ClockFactory;
use OpenTelemetry\SDK\Metrics\MeterProviderInterface;
use OpenTelemetry\SDK\Trace\SpanExporterInterface;
class BatchSpanProcessorBuilder
{
private SpanExporterInterface $exporter;
private ?MeterProviderInterface $meterProvider = null;
public function __construct(SpanExporterInterface $exporter)
{
$this->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
);
}
}

View File

@ -13,7 +13,6 @@ use OpenTelemetry\SDK\Common\Instrumentation\InstrumentationScopeFactory;
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 @@ final class TracerProvider implements TracerProviderInterface
?string $schemaUrl = null,
iterable $attributes = []
): API\TracerInterface {
if ($this->tracerSharedState->hasShutdown() || SDK::isDisabled()) {
if ($this->tracerSharedState->hasShutdown()) {
return NoopTracer::getInstance();
}
@ -92,4 +91,9 @@ final class TracerProvider implements TracerProviderInterface
return $this->tracerSharedState->shutdown($cancellation);
}
public static function builder(): TracerProviderBuilder
{
return new TracerProviderBuilder();
}
}

View File

@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace OpenTelemetry\SDK\Trace;
use OpenTelemetry\SDK\Resource\ResourceInfo;
class TracerProviderBuilder
{
// @var array<SpanProcessorInterface>
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,
);
}
}

View File

@ -5,7 +5,7 @@ declare(strict_types=1);
namespace OpenTelemetry\SDK\Trace;
use OpenTelemetry\SDK\Behavior\LogsMessagesTrait;
use OpenTelemetry\SDK\SDK;
use OpenTelemetry\SDK\Sdk;
final class TracerProviderFactory
{
@ -28,7 +28,7 @@ final class TracerProviderFactory
public function create(): TracerProviderInterface
{
if (SDK::isDisabled()) {
if (Sdk::isDisabled()) {
return new NoopTracerProvider();
}

View File

@ -20,6 +20,7 @@ use OpenTelemetry\SDK\Trace\SpanLimitsBuilder;
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 @@ class TracerTest extends TestCase
$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);
}

View File

@ -18,7 +18,7 @@ class SdkTest extends TestCase
{
$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));

View File

@ -35,7 +35,7 @@ class ResourceInfoTest extends TestCase
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);

View File

@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace OpenTelemetry\Tests\Unit\SDK;
use OpenTelemetry\API\Common\Instrumentation\Globals;
use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface;
use OpenTelemetry\SDK\Metrics\MeterProviderInterface;
use OpenTelemetry\SDK\SdkBuilder;
use OpenTelemetry\SDK\Trace\TracerProviderInterface;
use PHPUnit\Framework\TestCase;
/**
* @covers \OpenTelemetry\SDK\SdkBuilder
*/
class SdkBuilderTest extends TestCase
{
private TextMapPropagatorInterface $propagator;
private TracerProviderInterface $tracerProvider;
private MeterProviderInterface $meterProvider;
private SdkBuilder $builder;
public function setUp(): void
{
$this->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();
}
}

View File

@ -5,11 +5,15 @@ declare(strict_types=1);
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 @@ class SdkTest extends TestCase
public function test_is_not_disabled_by_default(): void
{
$this->assertFalse(SDK::isDisabled());
$this->assertFalse(Sdk::isDisabled());
}
/**
@ -31,7 +35,7 @@ class SdkTest extends TestCase
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 @@ class SdkTest extends TestCase
['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());
}
}