From 56c94749b51e529214fc93a5f65e5919a620145f Mon Sep 17 00:00:00 2001 From: Brett McBride Date: Thu, 9 Jan 2025 13:44:08 +1100 Subject: [PATCH 1/8] 2.x prep (#1463) Initial config changes for 2.x versions: * update to php 8.2 minimum * gitsplit 2.x * skip rector ReadOnlyClassRector --- composer.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index 1fe3d71..eb961e1 100644 --- a/composer.json +++ b/composer.json @@ -17,10 +17,10 @@ } ], "require": { - "php": "^8.1", + "php": "^8.2", "ext-json": "*", "nyholm/psr7-server": "^1.1", - "open-telemetry/api": "~1.0 || ~1.1", + "open-telemetry/api": "^1.0", "open-telemetry/context": "^1.0", "open-telemetry/sem-conv": "^1.0", "php-http/discovery": "^1.14", @@ -31,7 +31,6 @@ "psr/log": "^1.1|^2.0|^3.0", "ramsey/uuid": "^3.0 || ^4.0", "symfony/polyfill-mbstring": "^1.23", - "symfony/polyfill-php82": "^1.26", "tbachert/spi": "^1.0.1" }, "autoload": { @@ -55,7 +54,8 @@ }, "extra": { "branch-alias": { - "dev-main": "1.0.x-dev" + "dev-main": "1.0.x-dev", + "dev-2.x": "2.x-dev" }, "spi": { "OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\HookManagerInterface": [ From a45c75f0add671714a021ab51cf69e018cae4aee Mon Sep 17 00:00:00 2001 From: Brett McBride Date: Thu, 9 Jan 2025 15:25:08 +1100 Subject: [PATCH 2/8] allow 2.x versions of packages (#1472) --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index eb961e1..f6bfeb4 100644 --- a/composer.json +++ b/composer.json @@ -20,8 +20,8 @@ "php": "^8.2", "ext-json": "*", "nyholm/psr7-server": "^1.1", - "open-telemetry/api": "^1.0", - "open-telemetry/context": "^1.0", + "open-telemetry/api": "^1.0|^2.0", + "open-telemetry/context": "^1.0|^2.0", "open-telemetry/sem-conv": "^1.0", "php-http/discovery": "^1.14", "psr/http-client-implementation": "^1.0", From c568df5379e3861f612435c7d7dadcfaf5d46100 Mon Sep 17 00:00:00 2001 From: Brett McBride Date: Tue, 14 Jan 2025 19:44:05 +1100 Subject: [PATCH 3/8] reduce default set of resource detectors (#1471) Limit the default detectors from everything to: - sdk - sdk_provided - env This is controlled by a new detector key, OTEL_PHP_DETECTORS=default (which is the default if no value is provided). The previous behaviour can be restored by explicitly setting OTEL_PHP_DETECTORS=all. --- Common/Configuration/Defaults.php | 2 +- Common/Configuration/KnownValues.php | 2 ++ Resource/ResourceInfoFactory.php | 6 ++++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Common/Configuration/Defaults.php b/Common/Configuration/Defaults.php index 54d3e45..2445855 100644 --- a/Common/Configuration/Defaults.php +++ b/Common/Configuration/Defaults.php @@ -113,7 +113,7 @@ interface Defaults * @see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/configuration/sdk-environment-variables.md#language-specific-environment-variables */ public const OTEL_PHP_TRACES_PROCESSOR = 'batch'; - public const OTEL_PHP_DETECTORS = 'all'; + public const OTEL_PHP_DETECTORS = 'default'; public const OTEL_PHP_AUTOLOAD_ENABLED = 'false'; public const OTEL_PHP_INTERNAL_METRICS_ENABLED = 'false'; public const OTEL_PHP_DISABLED_INSTRUMENTATIONS = []; diff --git a/Common/Configuration/KnownValues.php b/Common/Configuration/KnownValues.php index 02c8f2e..7b9fe5d 100644 --- a/Common/Configuration/KnownValues.php +++ b/Common/Configuration/KnownValues.php @@ -189,6 +189,7 @@ interface KnownValues public const VALUE_STDOUT = 'stdout'; public const VALUE_PSR3 = 'psr3'; public const VALUE_EMPTY = ''; + public const VALUE_DETECTORS_DEFAULT = 'default'; public const VALUE_DETECTORS_ENVIRONMENT = 'env'; public const VALUE_DETECTORS_HOST = 'host'; public const VALUE_DETECTORS_OS = 'os'; @@ -199,6 +200,7 @@ interface KnownValues public const VALUE_DETECTORS_SERVICE = 'service'; public const VALUE_DETECTORS_COMPOSER = 'composer'; public const OTEL_PHP_DETECTORS = [ + self::VALUE_DETECTORS_DEFAULT, self::VALUE_ALL, self::VALUE_DETECTORS_ENVIRONMENT, self::VALUE_DETECTORS_HOST, diff --git a/Resource/ResourceInfoFactory.php b/Resource/ResourceInfoFactory.php index 62759dd..a13e612 100644 --- a/Resource/ResourceInfoFactory.php +++ b/Resource/ResourceInfoFactory.php @@ -42,6 +42,12 @@ public static function defaultResource(): ResourceInfo foreach ($detectors as $detector) { switch ($detector) { + case Values::VALUE_DETECTORS_DEFAULT: + $resourceDetectors[] = new Detectors\Sdk(); + $resourceDetectors[] = new Detectors\SdkProvided(); + $resourceDetectors[] = new Detectors\Environment(); + + break; case Values::VALUE_DETECTORS_SERVICE: $resourceDetectors[] = new Detectors\Service(); From 0b255cab7061712410b659d6057e8819ad79cd94 Mon Sep 17 00:00:00 2001 From: Brett McBride Date: Thu, 23 Jan 2025 11:11:47 +1100 Subject: [PATCH 4/8] implement mutable ReadWriteLogRecord (#1482) https://github.com/open-telemetry/opentelemetry-specification/pull/3907 implements some new requirements for logging: - ReadWriteLogRecord can mutate (eg by processors) - mutated ReadWriteLogRecord can be seen by later processors This is a breaking change because LogRecordProcessorInterface onEmit param changes to by-reference --- Logs/Exporter/InMemoryExporter.php | 18 +++++++++++++++++- Logs/LogRecordProcessorInterface.php | 2 +- Logs/Processor/BatchLogRecordProcessor.php | 2 +- Logs/Processor/MultiLogRecordProcessor.php | 2 +- Logs/Processor/NoopLogRecordProcessor.php | 2 +- Logs/Processor/SimpleLogRecordProcessor.php | 2 +- Logs/ReadWriteLogRecord.php | 13 +++++++++++++ Logs/ReadableLogRecord.php | 11 +++++------ 8 files changed, 40 insertions(+), 12 deletions(-) diff --git a/Logs/Exporter/InMemoryExporter.php b/Logs/Exporter/InMemoryExporter.php index 3ecaecd..52615d2 100644 --- a/Logs/Exporter/InMemoryExporter.php +++ b/Logs/Exporter/InMemoryExporter.php @@ -9,6 +9,7 @@ use OpenTelemetry\SDK\Common\Future\CompletedFuture; use OpenTelemetry\SDK\Common\Future\FutureInterface; use OpenTelemetry\SDK\Logs\LogRecordExporterInterface; +use OpenTelemetry\SDK\Logs\ReadableLogRecord; class InMemoryExporter implements LogRecordExporterInterface { @@ -22,7 +23,7 @@ public function __construct(private readonly ArrayObject $storage = new ArrayObj public function export(iterable $batch, ?CancellationInterface $cancellation = null): FutureInterface { foreach ($batch as $record) { - $this->storage->append($record); + $this->storage->append($this->convert($record)); } return new CompletedFuture(true); @@ -42,4 +43,19 @@ public function getStorage(): ArrayObject { return $this->storage; } + + private function convert(ReadableLogRecord $record): array + { + return [ + 'timestamp' => $record->getTimestamp(), + 'observed_timestamp' => $record->getObservedTimestamp(), + 'severity_number' => $record->getSeverityNumber(), + 'severity_text' => $record->getSeverityText(), + 'body' => $record->getBody(), + 'attributes' => $record->getAttributes()->toArray(), + 'trace_id' => $record->getSpanContext()?->getTraceId(), + 'span_id' => $record->getSpanContext()?->getSpanId(), + 'trace_flags' => $record->getSpanContext()?->getTraceFlags(), + ]; + } } diff --git a/Logs/LogRecordProcessorInterface.php b/Logs/LogRecordProcessorInterface.php index 1977d48..ba1519a 100644 --- a/Logs/LogRecordProcessorInterface.php +++ b/Logs/LogRecordProcessorInterface.php @@ -9,7 +9,7 @@ interface LogRecordProcessorInterface { - public function onEmit(ReadWriteLogRecord $record, ?ContextInterface $context = null): void; + public function onEmit(ReadWriteLogRecord &$record, ?ContextInterface $context = null): void; public function shutdown(?CancellationInterface $cancellation = null): bool; public function forceFlush(?CancellationInterface $cancellation = null): bool; } diff --git a/Logs/Processor/BatchLogRecordProcessor.php b/Logs/Processor/BatchLogRecordProcessor.php index 8de2d0f..6fcd23e 100644 --- a/Logs/Processor/BatchLogRecordProcessor.php +++ b/Logs/Processor/BatchLogRecordProcessor.php @@ -134,7 +134,7 @@ public function __construct( }); } - public function onEmit(ReadWriteLogRecord $record, ?ContextInterface $context = null): void + public function onEmit(ReadWriteLogRecord &$record, ?ContextInterface $context = null): void { if ($this->closed) { return; diff --git a/Logs/Processor/MultiLogRecordProcessor.php b/Logs/Processor/MultiLogRecordProcessor.php index 6a5791f..4bb6c54 100644 --- a/Logs/Processor/MultiLogRecordProcessor.php +++ b/Logs/Processor/MultiLogRecordProcessor.php @@ -22,7 +22,7 @@ public function __construct(array $processors) } } - public function onEmit(ReadWriteLogRecord $record, ?ContextInterface $context = null): void + public function onEmit(ReadWriteLogRecord &$record, ?ContextInterface $context = null): void { foreach ($this->processors as $processor) { $processor->onEmit($record, $context); diff --git a/Logs/Processor/NoopLogRecordProcessor.php b/Logs/Processor/NoopLogRecordProcessor.php index 7028052..3fabcfd 100644 --- a/Logs/Processor/NoopLogRecordProcessor.php +++ b/Logs/Processor/NoopLogRecordProcessor.php @@ -21,7 +21,7 @@ public static function getInstance(): self /** * @codeCoverageIgnore */ - public function onEmit(ReadWriteLogRecord $record, ?ContextInterface $context = null): void + public function onEmit(ReadWriteLogRecord &$record, ?ContextInterface $context = null): void { } diff --git a/Logs/Processor/SimpleLogRecordProcessor.php b/Logs/Processor/SimpleLogRecordProcessor.php index 264a450..6df8a3f 100644 --- a/Logs/Processor/SimpleLogRecordProcessor.php +++ b/Logs/Processor/SimpleLogRecordProcessor.php @@ -19,7 +19,7 @@ public function __construct(private readonly LogRecordExporterInterface $exporte /** * @see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/sdk.md#onemit */ - public function onEmit(ReadWriteLogRecord $record, ?ContextInterface $context = null): void + public function onEmit(ReadWriteLogRecord &$record, ?ContextInterface $context = null): void { $this->exporter->export([$record]); } diff --git a/Logs/ReadWriteLogRecord.php b/Logs/ReadWriteLogRecord.php index 9bb4b15..74e1fb4 100644 --- a/Logs/ReadWriteLogRecord.php +++ b/Logs/ReadWriteLogRecord.php @@ -6,4 +6,17 @@ class ReadWriteLogRecord extends ReadableLogRecord { + public function setAttribute(string $name, mixed $value): self + { + $this->attributesBuilder->offsetSet($name, $value); + + return $this; + } + + public function removeAttribute(string $key): self + { + $this->attributesBuilder->offsetUnset($key); + + return $this; + } } diff --git a/Logs/ReadableLogRecord.php b/Logs/ReadableLogRecord.php index f1d09e5..e11839f 100644 --- a/Logs/ReadableLogRecord.php +++ b/Logs/ReadableLogRecord.php @@ -9,6 +9,7 @@ use OpenTelemetry\API\Trace\SpanContextInterface; use OpenTelemetry\Context\Context; use OpenTelemetry\Context\ContextInterface; +use OpenTelemetry\SDK\Common\Attribute\AttributesBuilderInterface; use OpenTelemetry\SDK\Common\Attribute\AttributesInterface; use OpenTelemetry\SDK\Common\Attribute\LogRecordAttributeValidator; use OpenTelemetry\SDK\Common\Instrumentation\InstrumentationScopeInterface; @@ -20,7 +21,7 @@ */ class ReadableLogRecord extends LogRecord { - protected AttributesInterface $convertedAttributes; + protected AttributesBuilderInterface $attributesBuilder; protected SpanContextInterface $spanContext; public function __construct( @@ -38,12 +39,10 @@ public function __construct( $this->severityNumber = $logRecord->severityNumber; $this->severityText = $logRecord->severityText; - //convert attributes now so that excess data is not sent to processors - $this->convertedAttributes = $this->loggerSharedState + $this->attributesBuilder = $this->loggerSharedState ->getLogRecordLimits() ->getAttributeFactory() - ->builder($logRecord->attributes, new LogRecordAttributeValidator()) - ->build(); + ->builder($logRecord->attributes, new LogRecordAttributeValidator()); } public function getInstrumentationScope(): InstrumentationScopeInterface @@ -96,6 +95,6 @@ public function getBody() public function getAttributes(): AttributesInterface { - return $this->convertedAttributes; + return $this->attributesBuilder->build(); } } From d330d84a9ecdd8918efc52d006efc6d871bff699 Mon Sep 17 00:00:00 2001 From: Brett McBride Date: Wed, 29 Jan 2025 12:50:05 +1100 Subject: [PATCH 5/8] use SPI to manage components (#1412) 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 --- Common/Export/Http/PsrTransportFactory.php | 10 + Common/Export/Http/PsrUtils.php | 4 +- .../Export/Stream/StreamTransportFactory.php | 10 + Common/Export/TransportFactoryInterface.php | 4 +- Common/Services/Loader.php | 99 +++++++++ Common/Services/SpiLoadableInterface.php | 11 + Logs/Exporter/ConsoleExporterFactory.php | 14 +- Logs/Exporter/InMemoryExporterFactory.php | 10 + Logs/Exporter/_register.php | 6 - Logs/ExporterFactory.php | 4 +- Logs/LogRecordExporterFactoryInterface.php | 4 +- Metrics/MeterProviderFactory.php | 4 +- .../ConsoleMetricExporterFactory.php | 10 + .../InMemoryExporterFactory.php | 10 + .../NoopMetricExporterFactory.php | 10 + Metrics/MetricExporter/_register.php | 7 - Metrics/MetricExporterFactoryInterface.php | 4 +- Propagation/B3MultiPropagatorFactory.php | 28 +++ Propagation/B3PropagatorFactory.php | 28 +++ Propagation/BaggagePropagatorFactory.php | 29 +++ .../CloudTraceOneWayPropagatorFactory.php | 28 +++ Propagation/CloudTracePropagatorFactory.php | 28 +++ .../JaegerBaggagePropagatorFactory.php | 28 +++ Propagation/JaegerPropagatorFactory.php | 28 +++ Propagation/PropagatorFactory.php | 17 +- .../TextMapPropagatorFactoryInterface.php | 13 ++ Propagation/TraceContextPropagatorFactory.php | 29 +++ Propagation/_register.php | 16 -- Registry.php | 205 ------------------ Resource/ResourceDetectorFactoryInterface.php | 12 + Resource/ResourceInfoFactory.php | 6 +- Trace/ExporterFactory.php | 4 +- .../ConsoleSpanExporterFactory.php | 14 +- .../InMemorySpanExporterFactory.php | 10 + .../SpanExporterFactoryInterface.php | 3 +- Trace/SpanExporter/_register.php | 7 - _register.php | 45 ++++ composer.json | 14 +- 38 files changed, 540 insertions(+), 273 deletions(-) create mode 100644 Common/Services/Loader.php create mode 100644 Common/Services/SpiLoadableInterface.php delete mode 100644 Logs/Exporter/_register.php delete mode 100644 Metrics/MetricExporter/_register.php create mode 100644 Propagation/B3MultiPropagatorFactory.php create mode 100644 Propagation/B3PropagatorFactory.php create mode 100644 Propagation/BaggagePropagatorFactory.php create mode 100644 Propagation/CloudTraceOneWayPropagatorFactory.php create mode 100644 Propagation/CloudTracePropagatorFactory.php create mode 100644 Propagation/JaegerBaggagePropagatorFactory.php create mode 100644 Propagation/JaegerPropagatorFactory.php create mode 100644 Propagation/TextMapPropagatorFactoryInterface.php create mode 100644 Propagation/TraceContextPropagatorFactory.php delete mode 100644 Propagation/_register.php delete mode 100644 Registry.php create mode 100644 Resource/ResourceDetectorFactoryInterface.php delete mode 100644 Trace/SpanExporter/_register.php create mode 100644 _register.php diff --git a/Common/Export/Http/PsrTransportFactory.php b/Common/Export/Http/PsrTransportFactory.php index 433ef7c..918762e 100644 --- a/Common/Export/Http/PsrTransportFactory.php +++ b/Common/Export/Http/PsrTransportFactory.php @@ -74,4 +74,14 @@ public static function discover(): self Psr17FactoryDiscovery::findStreamFactory(), ); } + + public function type(): string + { + return 'http'; + } + + public function priority(): int + { + return 0; + } } diff --git a/Common/Export/Http/PsrUtils.php b/Common/Export/Http/PsrUtils.php index 1639ba4..349090c 100644 --- a/Common/Export/Http/PsrUtils.php +++ b/Common/Export/Http/PsrUtils.php @@ -115,9 +115,9 @@ public static function decode(string $value, array $encodings): string public static function compression($compression): array { if (is_array($compression)) { - return $compression; + return array_filter($compression, fn ($value): bool => $value !== 'none'); } - if (!$compression) { + if ($compression === 'none' || !$compression) { return []; } if (!str_contains((string) $compression, ',')) { diff --git a/Common/Export/Stream/StreamTransportFactory.php b/Common/Export/Stream/StreamTransportFactory.php index c6544bb..1568e78 100644 --- a/Common/Export/Stream/StreamTransportFactory.php +++ b/Common/Export/Stream/StreamTransportFactory.php @@ -116,4 +116,14 @@ private static function createHeaderArray(string $contentType, array $headers): return $header; } + + public function type(): string + { + return 'stream'; + } + + public function priority(): int + { + return 0; + } } diff --git a/Common/Export/TransportFactoryInterface.php b/Common/Export/TransportFactoryInterface.php index a2dd3a1..92ad498 100644 --- a/Common/Export/TransportFactoryInterface.php +++ b/Common/Export/TransportFactoryInterface.php @@ -4,7 +4,9 @@ namespace OpenTelemetry\SDK\Common\Export; -interface TransportFactoryInterface +use OpenTelemetry\SDK\Common\Services\SpiLoadableInterface; + +interface TransportFactoryInterface extends SpiLoadableInterface { public const COMPRESSION_GZIP = 'gzip'; public const COMPRESSION_DEFLATE = 'deflate'; diff --git a/Common/Services/Loader.php b/Common/Services/Loader.php new file mode 100644 index 0000000..b4915a1 --- /dev/null +++ b/Common/Services/Loader.php @@ -0,0 +1,99 @@ + $class + * @phan-suppress PhanTypeNonVarPassByRef + */ + private static function getFactory(string $class, string $type): mixed + { + $factories = iterator_to_array(ServiceLoader::load($class)); + array_multisort( + array_map(static fn ($factory) => $factory->priority(), $factories), + SORT_DESC, + $factories, + ); + foreach ($factories as $factory) { + if ($factory->type() === $type) { + return $factory; + } + } + + return null; + } + + public static function spanExporterFactory(string $exporter): SpanExporterFactoryInterface + { + return self::getFactory(SpanExporterFactoryInterface::class, $exporter) + ?? throw new RuntimeException('Span exporter factory not defined for: ' . $exporter); + } + + public static function logRecordExporterFactory(string $exporter): LogRecordExporterFactoryInterface + { + return self::getFactory(LogRecordExporterFactoryInterface::class, $exporter) + ?? throw new RuntimeException('LogRecord exporter factory not defined for: ' . $exporter); + } + + /** + * Get transport factory registered for protocol. If $protocol contains a content-type eg `http/xyz` then + * only the first part, `http`, is used. + */ + public static function transportFactory(string $protocol): TransportFactoryInterface + { + $type = explode('/', $protocol)[0]; + + return self::getFactory(TransportFactoryInterface::class, $type) + ?? throw new RuntimeException('Transport factory not defined for protocol: ' . $type); + } + + public static function metricExporterFactory(string $exporter): MetricExporterFactoryInterface + { + return self::getFactory(MetricExporterFactoryInterface::class, $exporter) + ?? throw new RuntimeException('Metric exporter factory not registered for protocol: ' . $exporter); + } + + public static function textMapPropagator(string $name): TextMapPropagatorInterface + { + $factory = self::getFactory(TextMapPropagatorFactoryInterface::class, $name) + ?? throw new RuntimeException('Text map propagator not registered for: ' . $name); + + return $factory->create(); + } + + public static function resourceDetector(string $name): ResourceDetectorInterface + { + $factory = self::getFactory(ResourceDetectorFactoryInterface::class, $name) + ?? throw new RuntimeException('Resource detector not registered for: ' . $name); + + return $factory->create(); + } + + /** + * @return ResourceDetectorInterface[] + */ + public static function resourceDetectors(): array + { + $factories = iterator_to_array(ServiceLoader::load(ResourceDetectorFactoryInterface::class)); + + return array_map(fn (ResourceDetectorFactoryInterface $factory) => $factory->create(), $factories); + } +} diff --git a/Common/Services/SpiLoadableInterface.php b/Common/Services/SpiLoadableInterface.php new file mode 100644 index 0000000..714e412 --- /dev/null +++ b/Common/Services/SpiLoadableInterface.php @@ -0,0 +1,11 @@ +create('php://stdout', 'application/json'); + $transport = Loader::transportFactory('stream')->create('php://stdout', 'application/json'); return new ConsoleExporter($transport); } + + public function type(): string + { + return 'console'; + } + + public function priority(): int + { + return 0; + } } diff --git a/Logs/Exporter/InMemoryExporterFactory.php b/Logs/Exporter/InMemoryExporterFactory.php index 6f24def..aad5389 100644 --- a/Logs/Exporter/InMemoryExporterFactory.php +++ b/Logs/Exporter/InMemoryExporterFactory.php @@ -13,4 +13,14 @@ public function create(): LogRecordExporterInterface { return new InMemoryExporter(); } + + public function type(): string + { + return 'memory'; + } + + public function priority(): int + { + return 0; + } } diff --git a/Logs/Exporter/_register.php b/Logs/Exporter/_register.php deleted file mode 100644 index 96958ba..0000000 --- a/Logs/Exporter/_register.php +++ /dev/null @@ -1,6 +0,0 @@ -create(); } diff --git a/Logs/LogRecordExporterFactoryInterface.php b/Logs/LogRecordExporterFactoryInterface.php index 523bec1..153be99 100644 --- a/Logs/LogRecordExporterFactoryInterface.php +++ b/Logs/LogRecordExporterFactoryInterface.php @@ -4,7 +4,9 @@ namespace OpenTelemetry\SDK\Logs; -interface LogRecordExporterFactoryInterface +use OpenTelemetry\SDK\Common\Services\SpiLoadableInterface; + +interface LogRecordExporterFactoryInterface extends SpiLoadableInterface { public function create(): LogRecordExporterInterface; } diff --git a/Metrics/MeterProviderFactory.php b/Metrics/MeterProviderFactory.php index 0f62cec..993812b 100644 --- a/Metrics/MeterProviderFactory.php +++ b/Metrics/MeterProviderFactory.php @@ -9,13 +9,13 @@ use OpenTelemetry\SDK\Common\Configuration\Configuration; use OpenTelemetry\SDK\Common\Configuration\KnownValues; use OpenTelemetry\SDK\Common\Configuration\Variables; +use OpenTelemetry\SDK\Common\Services\Loader; use OpenTelemetry\SDK\Metrics\Exemplar\ExemplarFilter\AllExemplarFilter; use OpenTelemetry\SDK\Metrics\Exemplar\ExemplarFilter\NoneExemplarFilter; use OpenTelemetry\SDK\Metrics\Exemplar\ExemplarFilter\WithSampledTraceExemplarFilter; use OpenTelemetry\SDK\Metrics\Exemplar\ExemplarFilterInterface; use OpenTelemetry\SDK\Metrics\MetricExporter\NoopMetricExporter; use OpenTelemetry\SDK\Metrics\MetricReader\ExportingReader; -use OpenTelemetry\SDK\Registry; use OpenTelemetry\SDK\Resource\ResourceInfo; use OpenTelemetry\SDK\Resource\ResourceInfoFactory; use OpenTelemetry\SDK\Sdk; @@ -42,7 +42,7 @@ public function create(?ResourceInfo $resource = null): MeterProviderInterface $exporterName = $exporters[0]; try { - $factory = Registry::metricExporterFactory($exporterName); + $factory = Loader::metricExporterFactory($exporterName); $exporter = $factory->create(); } catch (\Throwable $t) { self::logWarning(sprintf('Unable to create %s meter provider: %s', $exporterName, $t->getMessage())); diff --git a/Metrics/MetricExporter/ConsoleMetricExporterFactory.php b/Metrics/MetricExporter/ConsoleMetricExporterFactory.php index 1908873..df29ce3 100644 --- a/Metrics/MetricExporter/ConsoleMetricExporterFactory.php +++ b/Metrics/MetricExporter/ConsoleMetricExporterFactory.php @@ -13,4 +13,14 @@ public function create(): MetricExporterInterface { return new ConsoleMetricExporter(); } + + public function type(): string + { + return 'console'; + } + + public function priority(): int + { + return 0; + } } diff --git a/Metrics/MetricExporter/InMemoryExporterFactory.php b/Metrics/MetricExporter/InMemoryExporterFactory.php index c72c7b1..f95eaa9 100644 --- a/Metrics/MetricExporter/InMemoryExporterFactory.php +++ b/Metrics/MetricExporter/InMemoryExporterFactory.php @@ -13,4 +13,14 @@ public function create(): MetricExporterInterface { return new InMemoryExporter(); } + + public function type(): string + { + return 'memory'; + } + + public function priority(): int + { + return 0; + } } diff --git a/Metrics/MetricExporter/NoopMetricExporterFactory.php b/Metrics/MetricExporter/NoopMetricExporterFactory.php index ab2ab2a..7abe564 100644 --- a/Metrics/MetricExporter/NoopMetricExporterFactory.php +++ b/Metrics/MetricExporter/NoopMetricExporterFactory.php @@ -13,4 +13,14 @@ public function create(): MetricExporterInterface { return new NoopMetricExporter(); } + + public function type(): string + { + return 'none'; + } + + public function priority(): int + { + return 0; + } } diff --git a/Metrics/MetricExporter/_register.php b/Metrics/MetricExporter/_register.php deleted file mode 100644 index fba543d..0000000 --- a/Metrics/MetricExporter/_register.php +++ /dev/null @@ -1,7 +0,0 @@ -getMessage()); + switch ($name) { + case KnownValues::VALUE_NOOP: + case KnownValues::VALUE_NONE: + return NoopTextMapPropagator::getInstance(); + default: + try { + return Loader::textMapPropagator($name); + } catch (\RuntimeException $e) { + self::logWarning($e->getMessage()); + } } return NoopTextMapPropagator::getInstance(); diff --git a/Propagation/TextMapPropagatorFactoryInterface.php b/Propagation/TextMapPropagatorFactoryInterface.php new file mode 100644 index 0000000..0285806 --- /dev/null +++ b/Propagation/TextMapPropagatorFactoryInterface.php @@ -0,0 +1,13 @@ + $factory - * @throws TypeError - */ - public static function registerTransportFactory(string $protocol, TransportFactoryInterface|string $factory, bool $clobber = false): void - { - if (!$clobber && array_key_exists($protocol, self::$transportFactories)) { - return; - } - if (!is_subclass_of($factory, TransportFactoryInterface::class)) { - throw new TypeError( - sprintf( - 'Cannot register transport factory: %s must exist and implement %s', - is_string($factory) ? $factory : $factory::class, - TransportFactoryInterface::class - ) - ); - } - self::$transportFactories[$protocol] = $factory; - } - - /** - * @param SpanExporterFactoryInterface|class-string $factory - * @throws TypeError - */ - public static function registerSpanExporterFactory(string $exporter, SpanExporterFactoryInterface|string $factory, bool $clobber = false): void - { - if (!$clobber && array_key_exists($exporter, self::$spanExporterFactories)) { - return; - } - if (!is_subclass_of($factory, SpanExporterFactoryInterface::class)) { - throw new TypeError( - sprintf( - 'Cannot register span exporter factory: %s must exist and implement %s', - is_string($factory) ? $factory : $factory::class, - SpanExporterFactoryInterface::class - ) - ); - } - self::$spanExporterFactories[$exporter] = $factory; - } - - /** - * @param MetricExporterFactoryInterface|class-string $factory - * @throws TypeError - */ - public static function registerMetricExporterFactory(string $exporter, MetricExporterFactoryInterface|string $factory, bool $clobber = false): void - { - if (!$clobber && array_key_exists($exporter, self::$metricExporterFactories)) { - return; - } - if (!is_subclass_of($factory, MetricExporterFactoryInterface::class)) { - throw new TypeError( - sprintf( - 'Cannot register metric factory: %s must exist and implement %s', - is_string($factory) ? $factory : $factory::class, - MetricExporterFactoryInterface::class - ) - ); - } - self::$metricExporterFactories[$exporter] = $factory; - } - - /** - * @param LogRecordExporterFactoryInterface|class-string $factory - * @throws TypeError - */ - public static function registerLogRecordExporterFactory(string $exporter, LogRecordExporterFactoryInterface|string $factory, bool $clobber = false): void - { - if (!$clobber && array_key_exists($exporter, self::$logRecordExporterFactories)) { - return; - } - if (!is_subclass_of($factory, LogRecordExporterFactoryInterface::class)) { - throw new TypeError( - sprintf( - 'Cannot register LogRecord exporter factory: %s must exist and implement %s', - is_string($factory) ? $factory : $factory::class, - LogRecordExporterFactoryInterface::class - ) - ); - } - self::$logRecordExporterFactories[$exporter] = $factory; - } - - public static function registerTextMapPropagator(string $name, TextMapPropagatorInterface $propagator, bool $clobber = false): void - { - if (!$clobber && array_key_exists($name, self::$textMapPropagators)) { - return; - } - self::$textMapPropagators[$name] = $propagator; - } - - public static function registerResourceDetector(string $name, ResourceDetectorInterface $detector): void - { - self::$resourceDetectors[$name] = $detector; - } - - public static function spanExporterFactory(string $exporter): SpanExporterFactoryInterface - { - if (!array_key_exists($exporter, self::$spanExporterFactories)) { - throw new RuntimeException('Span exporter factory not defined for: ' . $exporter); - } - $class = self::$spanExporterFactories[$exporter]; - $factory = (is_callable($class)) ? $class : new $class(); - assert($factory instanceof SpanExporterFactoryInterface); - - return $factory; - } - - public static function logRecordExporterFactory(string $exporter): LogRecordExporterFactoryInterface - { - if (!array_key_exists($exporter, self::$logRecordExporterFactories)) { - throw new RuntimeException('LogRecord exporter factory not defined for: ' . $exporter); - } - $class = self::$logRecordExporterFactories[$exporter]; - $factory = (is_callable($class)) ? $class : new $class(); - assert($factory instanceof LogRecordExporterFactoryInterface); - - return $factory; - } - - /** - * Get transport factory registered for protocol. If $protocol contains a content-type eg `http/xyz` then - * only the first part, `http`, is used. - */ - public static function transportFactory(string $protocol): TransportFactoryInterface - { - $protocol = explode('/', $protocol)[0]; - if (!array_key_exists($protocol, self::$transportFactories)) { - throw new RuntimeException('Transport factory not defined for protocol: ' . $protocol); - } - $class = self::$transportFactories[$protocol]; - $factory = (is_callable($class)) ? $class : new $class(); - assert($factory instanceof TransportFactoryInterface); - - return $factory; - } - - public static function metricExporterFactory(string $exporter): MetricExporterFactoryInterface - { - if (!array_key_exists($exporter, self::$metricExporterFactories)) { - throw new RuntimeException('Metric exporter factory not registered for protocol: ' . $exporter); - } - $class = self::$metricExporterFactories[$exporter]; - $factory = (is_callable($class)) ? $class : new $class(); - assert($factory instanceof MetricExporterFactoryInterface); - - return $factory; - } - - public static function textMapPropagator(string $name): TextMapPropagatorInterface - { - if (!array_key_exists($name, self::$textMapPropagators)) { - throw new RuntimeException('Text map propagator not registered for: ' . $name); - } - - return self::$textMapPropagators[$name]; - } - - public static function resourceDetector(string $name): ResourceDetectorInterface - { - if (!array_key_exists($name, self::$resourceDetectors)) { - throw new RuntimeException('Resource detector not registered for: ' . $name); - } - - return self::$resourceDetectors[$name]; - } - - /** - * @return array - */ - public static function resourceDetectors(): array - { - return array_values(self::$resourceDetectors); - } -} diff --git a/Resource/ResourceDetectorFactoryInterface.php b/Resource/ResourceDetectorFactoryInterface.php new file mode 100644 index 0000000..fd26988 --- /dev/null +++ b/Resource/ResourceDetectorFactoryInterface.php @@ -0,0 +1,12 @@ +getResource(); } @@ -90,7 +90,7 @@ public static function defaultResource(): ResourceInfo break; default: try { - $resourceDetectors[] = Registry::resourceDetector($detector); + $resourceDetectors[] = Loader::resourceDetector($detector); } catch (RuntimeException $e) { self::logWarning($e->getMessage()); } diff --git a/Trace/ExporterFactory.php b/Trace/ExporterFactory.php index 9b652cc..eb3efad 100644 --- a/Trace/ExporterFactory.php +++ b/Trace/ExporterFactory.php @@ -7,7 +7,7 @@ use InvalidArgumentException; use OpenTelemetry\SDK\Common\Configuration\Configuration; use OpenTelemetry\SDK\Common\Configuration\Variables; -use OpenTelemetry\SDK\Registry; +use OpenTelemetry\SDK\Common\Services\Loader; use RuntimeException; class ExporterFactory @@ -25,7 +25,7 @@ public function create(): ?SpanExporterInterface if ($exporter === 'none') { return null; } - $factory = Registry::spanExporterFactory($exporter); + $factory = Loader::spanExporterFactory($exporter); return $factory->create(); } diff --git a/Trace/SpanExporter/ConsoleSpanExporterFactory.php b/Trace/SpanExporter/ConsoleSpanExporterFactory.php index 7e45fb5..40b04cb 100644 --- a/Trace/SpanExporter/ConsoleSpanExporterFactory.php +++ b/Trace/SpanExporter/ConsoleSpanExporterFactory.php @@ -4,15 +4,25 @@ namespace OpenTelemetry\SDK\Trace\SpanExporter; -use OpenTelemetry\SDK\Registry; +use OpenTelemetry\SDK\Common\Services\Loader; use OpenTelemetry\SDK\Trace\SpanExporterInterface; class ConsoleSpanExporterFactory implements SpanExporterFactoryInterface { public function create(): SpanExporterInterface { - $transport = Registry::transportFactory('stream')->create('php://stdout', 'application/json'); + $transport = Loader::transportFactory('stream')->create('php://stdout', 'application/json'); return new ConsoleSpanExporter($transport); } + + public function type(): string + { + return 'console'; + } + + public function priority(): int + { + return 0; + } } diff --git a/Trace/SpanExporter/InMemorySpanExporterFactory.php b/Trace/SpanExporter/InMemorySpanExporterFactory.php index c19686f..473a82f 100644 --- a/Trace/SpanExporter/InMemorySpanExporterFactory.php +++ b/Trace/SpanExporter/InMemorySpanExporterFactory.php @@ -12,4 +12,14 @@ public function create(): SpanExporterInterface { return new InMemoryExporter(); } + + public function type(): string + { + return 'memory'; + } + + public function priority(): int + { + return 0; + } } diff --git a/Trace/SpanExporter/SpanExporterFactoryInterface.php b/Trace/SpanExporter/SpanExporterFactoryInterface.php index 8d44b35..98d24bd 100644 --- a/Trace/SpanExporter/SpanExporterFactoryInterface.php +++ b/Trace/SpanExporter/SpanExporterFactoryInterface.php @@ -4,9 +4,10 @@ namespace OpenTelemetry\SDK\Trace\SpanExporter; +use OpenTelemetry\SDK\Common\Services\SpiLoadableInterface; use OpenTelemetry\SDK\Trace\SpanExporterInterface; -interface SpanExporterFactoryInterface +interface SpanExporterFactoryInterface extends SpiLoadableInterface { public function create(): SpanExporterInterface; } diff --git a/Trace/SpanExporter/_register.php b/Trace/SpanExporter/_register.php deleted file mode 100644 index aad07be..0000000 --- a/Trace/SpanExporter/_register.php +++ /dev/null @@ -1,7 +0,0 @@ - Date: Thu, 30 Jan 2025 08:38:51 +1100 Subject: [PATCH 6/8] revert pass-by-reference to LogRecordProcessor::onEmit (#1492) add a test to demonstrate that mutations are still seen by later processors without by-reference --- Logs/LogRecordProcessorInterface.php | 2 +- Logs/Processor/BatchLogRecordProcessor.php | 2 +- Logs/Processor/MultiLogRecordProcessor.php | 2 +- Logs/Processor/NoopLogRecordProcessor.php | 2 +- Logs/Processor/SimpleLogRecordProcessor.php | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Logs/LogRecordProcessorInterface.php b/Logs/LogRecordProcessorInterface.php index ba1519a..1977d48 100644 --- a/Logs/LogRecordProcessorInterface.php +++ b/Logs/LogRecordProcessorInterface.php @@ -9,7 +9,7 @@ interface LogRecordProcessorInterface { - public function onEmit(ReadWriteLogRecord &$record, ?ContextInterface $context = null): void; + public function onEmit(ReadWriteLogRecord $record, ?ContextInterface $context = null): void; public function shutdown(?CancellationInterface $cancellation = null): bool; public function forceFlush(?CancellationInterface $cancellation = null): bool; } diff --git a/Logs/Processor/BatchLogRecordProcessor.php b/Logs/Processor/BatchLogRecordProcessor.php index 6fcd23e..8de2d0f 100644 --- a/Logs/Processor/BatchLogRecordProcessor.php +++ b/Logs/Processor/BatchLogRecordProcessor.php @@ -134,7 +134,7 @@ public function __construct( }); } - public function onEmit(ReadWriteLogRecord &$record, ?ContextInterface $context = null): void + public function onEmit(ReadWriteLogRecord $record, ?ContextInterface $context = null): void { if ($this->closed) { return; diff --git a/Logs/Processor/MultiLogRecordProcessor.php b/Logs/Processor/MultiLogRecordProcessor.php index 4bb6c54..6a5791f 100644 --- a/Logs/Processor/MultiLogRecordProcessor.php +++ b/Logs/Processor/MultiLogRecordProcessor.php @@ -22,7 +22,7 @@ public function __construct(array $processors) } } - public function onEmit(ReadWriteLogRecord &$record, ?ContextInterface $context = null): void + public function onEmit(ReadWriteLogRecord $record, ?ContextInterface $context = null): void { foreach ($this->processors as $processor) { $processor->onEmit($record, $context); diff --git a/Logs/Processor/NoopLogRecordProcessor.php b/Logs/Processor/NoopLogRecordProcessor.php index 3fabcfd..7028052 100644 --- a/Logs/Processor/NoopLogRecordProcessor.php +++ b/Logs/Processor/NoopLogRecordProcessor.php @@ -21,7 +21,7 @@ public static function getInstance(): self /** * @codeCoverageIgnore */ - public function onEmit(ReadWriteLogRecord &$record, ?ContextInterface $context = null): void + public function onEmit(ReadWriteLogRecord $record, ?ContextInterface $context = null): void { } diff --git a/Logs/Processor/SimpleLogRecordProcessor.php b/Logs/Processor/SimpleLogRecordProcessor.php index 6df8a3f..264a450 100644 --- a/Logs/Processor/SimpleLogRecordProcessor.php +++ b/Logs/Processor/SimpleLogRecordProcessor.php @@ -19,7 +19,7 @@ public function __construct(private readonly LogRecordExporterInterface $exporte /** * @see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/sdk.md#onemit */ - public function onEmit(ReadWriteLogRecord &$record, ?ContextInterface $context = null): void + public function onEmit(ReadWriteLogRecord $record, ?ContextInterface $context = null): void { $this->exporter->export([$record]); } From 797af046063ca048dcacae07d3275f95059ba7c4 Mon Sep 17 00:00:00 2001 From: Brett McBride Date: Thu, 30 Jan 2025 08:45:01 +1100 Subject: [PATCH 7/8] implement SpanProcessor::onEnding (#1483) * added to spec 1.36.0 * mutable span in OnEnding, prevent async mutation (a clone rather than the original span is passed to onEnd) * change composer/tools cache key cached vendor dir was causing havoc with a conflict between vendor and vendor-bin/psalm (amphp/amp) --- Trace/Span.php | 25 ++++++++++++++------- Trace/SpanProcessor/BatchSpanProcessor.php | 4 ++++ Trace/SpanProcessor/MultiSpanProcessor.php | 7 ++++++ Trace/SpanProcessor/NoopSpanProcessor.php | 4 ++++ Trace/SpanProcessor/SimpleSpanProcessor.php | 4 ++++ Trace/SpanProcessorInterface.php | 6 +++++ 6 files changed, 42 insertions(+), 8 deletions(-) diff --git a/Trace/Span.php b/Trace/Span.php index 894f193..130c9f2 100644 --- a/Trace/Span.php +++ b/Trace/Span.php @@ -24,7 +24,7 @@ final class Span extends API\Span implements ReadWriteSpanInterface private array $events = []; private int $totalRecordedEvents = 0; private StatusDataInterface $status; - private int $endEpochNanos = 0; + private ?int $endEpochNanos = null; private bool $hasEnded = false; /** @@ -138,6 +138,10 @@ public function setAttribute(string $key, $value): self /** @inheritDoc */ public function setAttributes(iterable $attributes): self { + if ($this->hasEnded) { + return $this; + } + foreach ($attributes as $key => $value) { $this->attributesBuilder[$key] = $value; } @@ -244,19 +248,24 @@ public function setStatus(string $code, ?string $description = null): self return $this; } - /** @inheritDoc */ + /** + * @inheritDoc + * @psalm-suppress NoInterfaceProperties,UndefinedInterfaceMethod + **/ public function end(?int $endEpochNanos = null): void { - if ($this->hasEnded) { + if ($this->endEpochNanos !== null) { return; } $this->endEpochNanos = $endEpochNanos ?? Clock::getDefault()->now(); - $this->hasEnded = true; - - $this->checkForDroppedElements(); + $span = clone $this; + $this->hasEnded = true; // prevent further modifications to the span by async code + $this->spanProcessor->onEnding($span); + $span->hasEnded = true; - $this->spanProcessor->onEnd($this); + $this->spanProcessor->onEnd($span); + $span->checkForDroppedElements(); } /** @inheritDoc */ @@ -291,7 +300,7 @@ public function toSpanData(): SpanDataInterface $this->totalRecordedLinks, $this->totalRecordedEvents, $this->status, - $this->endEpochNanos, + $this->endEpochNanos ?? 0, $this->hasEnded ); } diff --git a/Trace/SpanProcessor/BatchSpanProcessor.php b/Trace/SpanProcessor/BatchSpanProcessor.php index e1a3ff4..4bcc7b1 100644 --- a/Trace/SpanProcessor/BatchSpanProcessor.php +++ b/Trace/SpanProcessor/BatchSpanProcessor.php @@ -143,6 +143,10 @@ public function onStart(ReadWriteSpanInterface $span, ContextInterface $parentCo { } + public function onEnding(ReadWriteSpanInterface $span): void + { + } + public function onEnd(ReadableSpanInterface $span): void { if ($this->closed) { diff --git a/Trace/SpanProcessor/MultiSpanProcessor.php b/Trace/SpanProcessor/MultiSpanProcessor.php index e690791..da5cc59 100644 --- a/Trace/SpanProcessor/MultiSpanProcessor.php +++ b/Trace/SpanProcessor/MultiSpanProcessor.php @@ -76,4 +76,11 @@ public function forceFlush(?CancellationInterface $cancellation = null): bool return $result; } + + public function onEnding(ReadWriteSpanInterface $span): void + { + foreach ($this->processors as $processor) { + $processor->onEnding($span); + } + } } diff --git a/Trace/SpanProcessor/NoopSpanProcessor.php b/Trace/SpanProcessor/NoopSpanProcessor.php index 9c4d1ea..950f7ac 100644 --- a/Trace/SpanProcessor/NoopSpanProcessor.php +++ b/Trace/SpanProcessor/NoopSpanProcessor.php @@ -44,4 +44,8 @@ public function shutdown(?CancellationInterface $cancellation = null): bool { return $this->forceFlush(); } + + public function onEnding(ReadWriteSpanInterface $span): void + { + } //@codeCoverageIgnore } diff --git a/Trace/SpanProcessor/SimpleSpanProcessor.php b/Trace/SpanProcessor/SimpleSpanProcessor.php index 7b65e58..3511507 100644 --- a/Trace/SpanProcessor/SimpleSpanProcessor.php +++ b/Trace/SpanProcessor/SimpleSpanProcessor.php @@ -113,4 +113,8 @@ private function flush(Closure $task, string $taskName, bool $propagateResult, C return $success; } + + public function onEnding(ReadWriteSpanInterface $span): void + { + } } diff --git a/Trace/SpanProcessorInterface.php b/Trace/SpanProcessorInterface.php index 24bcea2..ab851b8 100644 --- a/Trace/SpanProcessorInterface.php +++ b/Trace/SpanProcessorInterface.php @@ -15,6 +15,12 @@ interface SpanProcessorInterface */ public function onStart(ReadWriteSpanInterface $span, ContextInterface $parentContext): void; + /** + * @experimental + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.41.0/specification/trace/sdk.md#onending + */ + public function onEnding(ReadWriteSpanInterface $span): void; + /** * @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.7.0/specification/trace/sdk.md#onendspan */ From d486283c70d8f5e7d11de9590550f7f497a89e3c Mon Sep 17 00:00:00 2001 From: Brett McBride Date: Thu, 13 Feb 2025 09:39:01 +1100 Subject: [PATCH 8/8] drop string options for Temporality, InstrumentType (#1493) * drop metrics string temporality Temporality|string|null was allowed for temporality to avoid BC in 1.x convert Temporality to an enum * convert InstrumentType to enum - drop string|InstrumentType, which exists in 1.x for forward-compatibility * add advisory to DefaultAggregationProviderInterface --- Metrics/AggregationInterface.php | 4 ++-- ...ggregationTemporalitySelectorInterface.php | 4 ++-- Metrics/Data/Histogram.php | 2 +- Metrics/Data/Sum.php | 2 +- Metrics/Data/Temporality.php | 13 +++-------- .../DefaultAggregationProviderInterface.php | 7 ++---- Metrics/DefaultAggregationProviderTrait.php | 3 +-- Metrics/Instrument.php | 2 +- Metrics/InstrumentType.php | 22 +++++++------------ Metrics/Meter.php | 5 ++--- .../MetricExporter/ConsoleMetricExporter.php | 4 ++-- Metrics/MetricExporter/InMemoryExporter.php | 4 ++-- .../StreamMetricSourceProvider.php | 6 +++-- Metrics/MetricMetadataInterface.php | 8 +++---- Metrics/MetricReader/ExportingReader.php | 4 ++-- Metrics/MetricSourceProviderInterface.php | 5 +---- Metrics/Stream/AsynchronousMetricStream.php | 2 +- Metrics/Stream/MetricStreamInterface.php | 8 +++---- Metrics/Stream/SynchronousMetricStream.php | 2 +- .../InstrumentTypeCriteria.php | 6 ++--- 20 files changed, 47 insertions(+), 66 deletions(-) diff --git a/Metrics/AggregationInterface.php b/Metrics/AggregationInterface.php index 7f2cb1e..e3707de 100644 --- a/Metrics/AggregationInterface.php +++ b/Metrics/AggregationInterface.php @@ -44,7 +44,7 @@ public function diff($left, $right); * @param array $attributes * @psalm-param array $summaries * @param array> $exemplars - * @param string|Temporality $temporality + * @param Temporality $temporality */ public function toData( array $attributes, @@ -52,6 +52,6 @@ public function toData( array $exemplars, int $startTimestamp, int $timestamp, - $temporality, + Temporality $temporality, ): DataInterface; } diff --git a/Metrics/AggregationTemporalitySelectorInterface.php b/Metrics/AggregationTemporalitySelectorInterface.php index f046d03..28d9896 100644 --- a/Metrics/AggregationTemporalitySelectorInterface.php +++ b/Metrics/AggregationTemporalitySelectorInterface.php @@ -14,8 +14,8 @@ interface AggregationTemporalitySelectorInterface * It is recommended to return {@see MetricMetadataInterface::temporality()} * if the exporter does not require a specific temporality. * - * @return string|Temporality|null temporality to use, or null to signal + * @return ?Temporality temporality to use, or null to signal * that the given metric should not be exported by this exporter */ - public function temporality(MetricMetadataInterface $metric); + public function temporality(MetricMetadataInterface $metric): ?Temporality; } diff --git a/Metrics/Data/Histogram.php b/Metrics/Data/Histogram.php index 49fdb78..154a4fc 100644 --- a/Metrics/Data/Histogram.php +++ b/Metrics/Data/Histogram.php @@ -11,7 +11,7 @@ final class Histogram implements DataInterface */ public function __construct( public readonly iterable $dataPoints, - public readonly string|Temporality $temporality, + public readonly Temporality $temporality, ) { } } diff --git a/Metrics/Data/Sum.php b/Metrics/Data/Sum.php index f4e32ea..314a392 100644 --- a/Metrics/Data/Sum.php +++ b/Metrics/Data/Sum.php @@ -11,7 +11,7 @@ final class Sum implements DataInterface */ public function __construct( public readonly iterable $dataPoints, - public readonly string|Temporality $temporality, + public readonly Temporality $temporality, public readonly bool $monotonic, ) { } diff --git a/Metrics/Data/Temporality.php b/Metrics/Data/Temporality.php index e51c557..a8d2a8c 100644 --- a/Metrics/Data/Temporality.php +++ b/Metrics/Data/Temporality.php @@ -6,16 +6,9 @@ /** * Metric aggregation temporality. - * - * Has to be type-hinted as `string|Temporality` to be forward compatible. - * @todo convert to enum (php >= 8.1) */ -final class Temporality +enum Temporality { - public const DELTA = 'Delta'; - public const CUMULATIVE = 'Cumulative'; - - private function __construct() - { - } + case DELTA; + case CUMULATIVE; } diff --git a/Metrics/DefaultAggregationProviderInterface.php b/Metrics/DefaultAggregationProviderInterface.php index f2405e9..cd362a6 100644 --- a/Metrics/DefaultAggregationProviderInterface.php +++ b/Metrics/DefaultAggregationProviderInterface.php @@ -7,11 +7,8 @@ interface DefaultAggregationProviderInterface { /** - * @param string|InstrumentType $instrumentType + * @param InstrumentType $instrumentType * @param array $advisory optional set of recommendations - * - * @noinspection PhpDocSignatureInspection not added for BC - * @phan-suppress PhanCommentParamWithoutRealParam @phpstan-ignore-next-line */ - public function defaultAggregation($instrumentType /*, array $advisory = [] */): ?AggregationInterface; + public function defaultAggregation(InstrumentType $instrumentType, array $advisory = []): ?AggregationInterface; } diff --git a/Metrics/DefaultAggregationProviderTrait.php b/Metrics/DefaultAggregationProviderTrait.php index 9d42129..8782c61 100644 --- a/Metrics/DefaultAggregationProviderTrait.php +++ b/Metrics/DefaultAggregationProviderTrait.php @@ -6,14 +6,13 @@ trait DefaultAggregationProviderTrait { - public function defaultAggregation($instrumentType, array $advisory = []): ?AggregationInterface + public function defaultAggregation(InstrumentType $instrumentType, array $advisory = []): ?AggregationInterface { return match ($instrumentType) { InstrumentType::COUNTER, InstrumentType::ASYNCHRONOUS_COUNTER => new Aggregation\SumAggregation(true), InstrumentType::UP_DOWN_COUNTER, InstrumentType::ASYNCHRONOUS_UP_DOWN_COUNTER => new Aggregation\SumAggregation(), InstrumentType::HISTOGRAM => new Aggregation\ExplicitBucketHistogramAggregation($advisory['ExplicitBucketBoundaries'] ?? [0, 5, 10, 25, 50, 75, 100, 250, 500, 1000]), InstrumentType::GAUGE, InstrumentType::ASYNCHRONOUS_GAUGE => new Aggregation\LastValueAggregation(), - default => null, }; } } diff --git a/Metrics/Instrument.php b/Metrics/Instrument.php index 8d9a5b3..cbc24bb 100644 --- a/Metrics/Instrument.php +++ b/Metrics/Instrument.php @@ -7,7 +7,7 @@ final class Instrument { public function __construct( - public readonly string|InstrumentType $type, + public readonly InstrumentType $type, public readonly string $name, public readonly ?string $unit, public readonly ?string $description, diff --git a/Metrics/InstrumentType.php b/Metrics/InstrumentType.php index a0b9aa9..3e333a5 100644 --- a/Metrics/InstrumentType.php +++ b/Metrics/InstrumentType.php @@ -6,22 +6,16 @@ /** * Instrument type. - * - * Has to be type-hinted as `string|InstrumentType` to be forward compatible. */ -final class InstrumentType +enum InstrumentType { - public const COUNTER = 'Counter'; - public const UP_DOWN_COUNTER = 'UpDownCounter'; - public const HISTOGRAM = 'Histogram'; + case COUNTER; + case UP_DOWN_COUNTER; + case HISTOGRAM; /** @experimental */ - public const GAUGE = 'Gauge'; + case GAUGE; - public const ASYNCHRONOUS_COUNTER = 'AsynchronousCounter'; - public const ASYNCHRONOUS_UP_DOWN_COUNTER = 'AsynchronousUpDownCounter'; - public const ASYNCHRONOUS_GAUGE = 'AsynchronousGauge'; - - private function __construct() - { - } + case ASYNCHRONOUS_COUNTER; + case ASYNCHRONOUS_UP_DOWN_COUNTER; + case ASYNCHRONOUS_GAUGE; } diff --git a/Metrics/Meter.php b/Metrics/Meter.php index 0f4cceb..eb0cbb8 100644 --- a/Metrics/Meter.php +++ b/Metrics/Meter.php @@ -294,7 +294,7 @@ private function getAsynchronousInstrument(Instrument $instrument, Instrumentati /** * @return array{Instrument, ReferenceCounterInterface, RegisteredInstrument} */ - private function createSynchronousWriter(string|InstrumentType $instrumentType, string $name, ?string $unit, ?string $description, array $advisory = []): array + private function createSynchronousWriter(InstrumentType $instrumentType, string $name, ?string $unit, ?string $description, array $advisory = []): array { $instrument = new Instrument($instrumentType, $name, $unit, $description, $advisory); @@ -340,7 +340,7 @@ private function createSynchronousWriter(string|InstrumentType $instrumentType, /** * @return array{Instrument, ReferenceCounterInterface, RegisteredInstrument} */ - private function createAsynchronousObserver(string|InstrumentType $instrumentType, string $name, ?string $unit, ?string $description, array $advisory): array + private function createAsynchronousObserver(InstrumentType $instrumentType, string $name, ?string $unit, ?string $description, array $advisory): array { $instrument = new Instrument($instrumentType, $name, $unit, $description, $advisory); @@ -420,7 +420,6 @@ private function viewRegistrationRequests(Instrument $instrument, StalenessHandl $view->unit, $view->description, $view->attributeKeys, - /** @phan-suppress-next-line PhanParamTooMany @phpstan-ignore-next-line */ $metricRegistry->defaultAggregation($instrument->type, $instrument->advisory), ), new RegistryRegistration($metricRegistry, $stalenessHandler), diff --git a/Metrics/MetricExporter/ConsoleMetricExporter.php b/Metrics/MetricExporter/ConsoleMetricExporter.php index 8b75771..e0eb750 100644 --- a/Metrics/MetricExporter/ConsoleMetricExporter.php +++ b/Metrics/MetricExporter/ConsoleMetricExporter.php @@ -18,13 +18,13 @@ */ class ConsoleMetricExporter implements PushMetricExporterInterface, AggregationTemporalitySelectorInterface { - public function __construct(private readonly Temporality|string|null $temporality = null) + public function __construct(private readonly ?Temporality $temporality = null) { } /** * @inheritDoc */ - public function temporality(MetricMetadataInterface $metric): Temporality|string|null + public function temporality(MetricMetadataInterface $metric): ?Temporality { return $this->temporality ?? $metric->temporality(); } diff --git a/Metrics/MetricExporter/InMemoryExporter.php b/Metrics/MetricExporter/InMemoryExporter.php index e0d7da4..d9e5b10 100644 --- a/Metrics/MetricExporter/InMemoryExporter.php +++ b/Metrics/MetricExporter/InMemoryExporter.php @@ -23,11 +23,11 @@ final class InMemoryExporter implements MetricExporterInterface, AggregationTemp private bool $closed = false; - public function __construct(private readonly string|Temporality|null $temporality = null) + public function __construct(private readonly ?Temporality $temporality = null) { } - public function temporality(MetricMetadataInterface $metric): string|Temporality|null + public function temporality(MetricMetadataInterface $metric): ?Temporality { return $this->temporality ?? $metric->temporality(); } diff --git a/Metrics/MetricFactory/StreamMetricSourceProvider.php b/Metrics/MetricFactory/StreamMetricSourceProvider.php index ec42fc4..f5984a7 100644 --- a/Metrics/MetricFactory/StreamMetricSourceProvider.php +++ b/Metrics/MetricFactory/StreamMetricSourceProvider.php @@ -5,7 +5,9 @@ namespace OpenTelemetry\SDK\Metrics\MetricFactory; use OpenTelemetry\SDK\Common\Instrumentation\InstrumentationScopeInterface; +use OpenTelemetry\SDK\Metrics\Data\Temporality; use OpenTelemetry\SDK\Metrics\Instrument; +use OpenTelemetry\SDK\Metrics\InstrumentType; use OpenTelemetry\SDK\Metrics\MetricMetadataInterface; use OpenTelemetry\SDK\Metrics\MetricRegistry\MetricCollectorInterface; use OpenTelemetry\SDK\Metrics\MetricSourceInterface; @@ -35,7 +37,7 @@ public function create($temporality): MetricSourceInterface return new StreamMetricSource($this, $this->stream->register($temporality)); } - public function instrumentType() + public function instrumentType(): InstrumentType { return $this->instrument->type; } @@ -55,7 +57,7 @@ public function description(): ?string return $this->view->description; } - public function temporality() + public function temporality(): Temporality { return $this->stream->temporality(); } diff --git a/Metrics/MetricMetadataInterface.php b/Metrics/MetricMetadataInterface.php index aa1a02d..44869e4 100644 --- a/Metrics/MetricMetadataInterface.php +++ b/Metrics/MetricMetadataInterface.php @@ -9,9 +9,9 @@ interface MetricMetadataInterface { /** - * @return string|InstrumentType + * @return InstrumentType */ - public function instrumentType(); + public function instrumentType(): InstrumentType; public function name(): string; @@ -22,7 +22,7 @@ public function description(): ?string; /** * Returns the underlying temporality of this metric. * - * @return string|Temporality internal temporality + * @return Temporality internal temporality */ - public function temporality(); + public function temporality(): Temporality; } diff --git a/Metrics/MetricReader/ExportingReader.php b/Metrics/MetricReader/ExportingReader.php index b1230d7..0899758 100644 --- a/Metrics/MetricReader/ExportingReader.php +++ b/Metrics/MetricReader/ExportingReader.php @@ -9,6 +9,7 @@ use OpenTelemetry\SDK\Metrics\AggregationTemporalitySelectorInterface; use OpenTelemetry\SDK\Metrics\DefaultAggregationProviderInterface; use OpenTelemetry\SDK\Metrics\DefaultAggregationProviderTrait; +use OpenTelemetry\SDK\Metrics\InstrumentType; use OpenTelemetry\SDK\Metrics\MetricExporterInterface; use OpenTelemetry\SDK\Metrics\MetricFactory\StreamMetricSourceProvider; use OpenTelemetry\SDK\Metrics\MetricMetadataInterface; @@ -39,10 +40,9 @@ public function __construct(private readonly MetricExporterInterface $exporter) { } - public function defaultAggregation($instrumentType, array $advisory = []): ?AggregationInterface + public function defaultAggregation(InstrumentType $instrumentType, array $advisory = []): ?AggregationInterface { if ($this->exporter instanceof DefaultAggregationProviderInterface) { - /** @phan-suppress-next-line PhanParamTooMany @phpstan-ignore-next-line */ return $this->exporter->defaultAggregation($instrumentType, $advisory); } diff --git a/Metrics/MetricSourceProviderInterface.php b/Metrics/MetricSourceProviderInterface.php index f8b6ea4..5a6c06d 100644 --- a/Metrics/MetricSourceProviderInterface.php +++ b/Metrics/MetricSourceProviderInterface.php @@ -8,8 +8,5 @@ interface MetricSourceProviderInterface { - /** - * @param string|Temporality $temporality - */ - public function create($temporality): MetricSourceInterface; + public function create(Temporality $temporality): MetricSourceInterface; } diff --git a/Metrics/Stream/AsynchronousMetricStream.php b/Metrics/Stream/AsynchronousMetricStream.php index a6a8930..6b2a04b 100644 --- a/Metrics/Stream/AsynchronousMetricStream.php +++ b/Metrics/Stream/AsynchronousMetricStream.php @@ -28,7 +28,7 @@ public function __construct( $this->metric = new Metric([], [], $startTimestamp); } - public function temporality(): Temporality|string + public function temporality(): Temporality { return Temporality::CUMULATIVE; } diff --git a/Metrics/Stream/MetricStreamInterface.php b/Metrics/Stream/MetricStreamInterface.php index 1373a1c..fb5785e 100644 --- a/Metrics/Stream/MetricStreamInterface.php +++ b/Metrics/Stream/MetricStreamInterface.php @@ -15,9 +15,9 @@ interface MetricStreamInterface /** * Returns the internal temporality of this stream. * - * @return string|Temporality internal temporality + * @return Temporality internal temporality */ - public function temporality(); + public function temporality(): Temporality; /** * Returns the last metric timestamp. @@ -36,10 +36,10 @@ public function push(Metric $metric): void; /** * Registers a new reader with the given temporality. * - * @param string|Temporality $temporality temporality to use + * @param Temporality $temporality temporality to use * @return int reader id */ - public function register($temporality): int; + public function register(Temporality $temporality): int; /** * Unregisters the given reader. diff --git a/Metrics/Stream/SynchronousMetricStream.php b/Metrics/Stream/SynchronousMetricStream.php index 2e12904..6a33824 100644 --- a/Metrics/Stream/SynchronousMetricStream.php +++ b/Metrics/Stream/SynchronousMetricStream.php @@ -39,7 +39,7 @@ public function __construct( $this->delta = new DeltaStorage($this->aggregation); } - public function temporality(): Temporality|string + public function temporality(): Temporality { return Temporality::DELTA; } diff --git a/Metrics/View/SelectionCriteria/InstrumentTypeCriteria.php b/Metrics/View/SelectionCriteria/InstrumentTypeCriteria.php index 35868e8..591961e 100644 --- a/Metrics/View/SelectionCriteria/InstrumentTypeCriteria.php +++ b/Metrics/View/SelectionCriteria/InstrumentTypeCriteria.php @@ -15,11 +15,11 @@ final class InstrumentTypeCriteria implements SelectionCriteriaInterface private readonly array $instrumentTypes; /** - * @param string|InstrumentType|string[]|InstrumentType[] $instrumentType + * @param InstrumentType|InstrumentType[] $instrumentType */ - public function __construct($instrumentType) + public function __construct(array|InstrumentType $instrumentType) { - $this->instrumentTypes = (array) $instrumentType; + $this->instrumentTypes = is_array($instrumentType) ? $instrumentType : [$instrumentType]; } public function accepts(Instrument $instrument, InstrumentationScopeInterface $instrumentationScope): bool