From 7ec0e904024679bf44ff354d92a2e8594002041a Mon Sep 17 00:00:00 2001 From: Tobias Bachert Date: Mon, 1 Apr 2024 09:13:01 +0200 Subject: [PATCH] File configuration (#1249) * Add configuration model * Add metrics and logs configuration model * Remove accidentally added package dependency * Fix `$properties` typehint to match config Default value available -> cannot be null. * Add configuration example that utilizes env substitution * Fix/suppress config psalm issues * Fix/suppress config phpstan issues * Suppress config phan issues * Apply cs-fixer * Change config package version to development version 0.x * Add README * Add basic test * Fix psalm and cs * Fix composer requirement for PRs * gitsplit, copy spi config to root composer.json --- .gitsplit.yml | 2 + .phan/config.php | 1 + composer.json | 56 ++- examples/load_config.php | 29 ++ examples/load_config.yaml | 44 ++ examples/load_config_env.php | 34 ++ examples/load_config_env.yaml | 49 +++ phpstan.neon.dist | 6 +- psalm.xml.dist | 30 +- .../Logs/LogRecordExporterConsole.php | 36 ++ .../Logs/LogRecordExporterOtlp.php | 75 ++++ .../Logs/LogRecordProcessorBatch.php | 60 +++ .../Logs/LogRecordProcessorSimple.php | 45 ++ .../Metrics/AggregationResolverDefault.php | 35 ++ .../Metrics/MetricExporterConsole.php | 32 ++ .../Metrics/MetricExporterOtlp.php | 92 ++++ .../Metrics/MetricReaderPeriodic.php | 49 +++ .../ComponentProvider/OpenTelemetrySdk.php | 400 ++++++++++++++++++ .../Propagator/TextMapPropagatorB3.php | 34 ++ .../Propagator/TextMapPropagatorB3Multi.php | 34 ++ .../Propagator/TextMapPropagatorBaggage.php | 32 ++ .../Propagator/TextMapPropagatorComposite.php | 38 ++ .../Propagator/TextMapPropagatorJaeger.php | 34 ++ .../TextMapPropagatorTraceContext.php | 32 ++ .../Trace/SamplerAlwaysOff.php | 32 ++ .../Trace/SamplerAlwaysOn.php | 32 ++ .../Trace/SamplerParentBased.php | 58 +++ .../Trace/SamplerTraceIdRatioBased.php | 43 ++ .../Trace/SpanExporterConsole.php | 36 ++ .../Trace/SpanExporterOtlp.php | 75 ++++ .../Trace/SpanExporterZipkin.php | 51 +++ .../Trace/SpanProcessorBatch.php | 60 +++ .../Trace/SpanProcessorSimple.php | 45 ++ src/Config/SDK/Configuration.php | 61 +++ src/Config/SDK/README.md | 34 ++ src/Config/SDK/composer.json | 65 +++ src/Contrib/Otlp/OtlpUtil.php | 17 + src/Contrib/Otlp/Protocols.php | 3 + .../Integration/Config/ConfigurationTest.php | 28 ++ .../Config/configurations/anchors.yaml | 41 ++ .../Config/configurations/kitchen-sink.yaml | 352 +++++++++++++++ 41 files changed, 2298 insertions(+), 14 deletions(-) create mode 100644 examples/load_config.php create mode 100644 examples/load_config.yaml create mode 100644 examples/load_config_env.php create mode 100644 examples/load_config_env.yaml create mode 100644 src/Config/SDK/ComponentProvider/Logs/LogRecordExporterConsole.php create mode 100644 src/Config/SDK/ComponentProvider/Logs/LogRecordExporterOtlp.php create mode 100644 src/Config/SDK/ComponentProvider/Logs/LogRecordProcessorBatch.php create mode 100644 src/Config/SDK/ComponentProvider/Logs/LogRecordProcessorSimple.php create mode 100644 src/Config/SDK/ComponentProvider/Metrics/AggregationResolverDefault.php create mode 100644 src/Config/SDK/ComponentProvider/Metrics/MetricExporterConsole.php create mode 100644 src/Config/SDK/ComponentProvider/Metrics/MetricExporterOtlp.php create mode 100644 src/Config/SDK/ComponentProvider/Metrics/MetricReaderPeriodic.php create mode 100644 src/Config/SDK/ComponentProvider/OpenTelemetrySdk.php create mode 100644 src/Config/SDK/ComponentProvider/Propagator/TextMapPropagatorB3.php create mode 100644 src/Config/SDK/ComponentProvider/Propagator/TextMapPropagatorB3Multi.php create mode 100644 src/Config/SDK/ComponentProvider/Propagator/TextMapPropagatorBaggage.php create mode 100644 src/Config/SDK/ComponentProvider/Propagator/TextMapPropagatorComposite.php create mode 100644 src/Config/SDK/ComponentProvider/Propagator/TextMapPropagatorJaeger.php create mode 100644 src/Config/SDK/ComponentProvider/Propagator/TextMapPropagatorTraceContext.php create mode 100644 src/Config/SDK/ComponentProvider/Trace/SamplerAlwaysOff.php create mode 100644 src/Config/SDK/ComponentProvider/Trace/SamplerAlwaysOn.php create mode 100644 src/Config/SDK/ComponentProvider/Trace/SamplerParentBased.php create mode 100644 src/Config/SDK/ComponentProvider/Trace/SamplerTraceIdRatioBased.php create mode 100644 src/Config/SDK/ComponentProvider/Trace/SpanExporterConsole.php create mode 100644 src/Config/SDK/ComponentProvider/Trace/SpanExporterOtlp.php create mode 100644 src/Config/SDK/ComponentProvider/Trace/SpanExporterZipkin.php create mode 100644 src/Config/SDK/ComponentProvider/Trace/SpanProcessorBatch.php create mode 100644 src/Config/SDK/ComponentProvider/Trace/SpanProcessorSimple.php create mode 100644 src/Config/SDK/Configuration.php create mode 100644 src/Config/SDK/README.md create mode 100644 src/Config/SDK/composer.json create mode 100644 tests/Integration/Config/ConfigurationTest.php create mode 100644 tests/Integration/Config/configurations/anchors.yaml create mode 100644 tests/Integration/Config/configurations/kitchen-sink.yaml diff --git a/.gitsplit.yml b/.gitsplit.yml index f21429504..9f7eb8289 100644 --- a/.gitsplit.yml +++ b/.gitsplit.yml @@ -16,6 +16,8 @@ splits: target: "https://${GH_TOKEN}@github.com/opentelemetry-php/api.git" - prefix: "src/SDK" target: "https://${GH_TOKEN}@github.com/opentelemetry-php/sdk.git" + - prefix: "src/Config/SDK" + target: "https://${GH_TOKEN}@github.com/opentelemetry-php/config-sdk.git" - prefix: "src/Contrib/Otlp" target: "https://${GH_TOKEN}@github.com/opentelemetry-php/exporter-otlp.git" - prefix: "src/Contrib/Grpc" diff --git a/.phan/config.php b/.phan/config.php index 13248372a..d8c6d47e0 100644 --- a/.phan/config.php +++ b/.phan/config.php @@ -319,6 +319,7 @@ 'exclude_analysis_directory_list' => [ 'vendor/', 'proto/', + 'src/Config/SDK', ], // Enable this to enable checks of require/include statements referring to valid paths. diff --git a/composer.json b/composer.json index 3865f61c3..b5521796a 100644 --- a/composer.json +++ b/composer.json @@ -16,14 +16,17 @@ "psr/http-message": "^1.0.1|^2.0", "psr/log": "^1.1|^2.0|^3.0", "symfony/polyfill-mbstring": "^1.23", - "symfony/polyfill-php82": "^1.26" + "symfony/polyfill-php82": "^1.26", + "tbachert/otel-sdk-configuration": "^0.1", + "tbachert/spi": "^0.2" }, "config": { "sort-packages": true, "allow-plugins": { "composer/package-versions-deprecated": true, "php-http/discovery": true, - "symfony/runtime": true + "symfony/runtime": true, + "tbachert/spi": true } }, "authors": [ @@ -33,12 +36,17 @@ } ], "replace": { - "open-telemetry/api": "self.version", - "open-telemetry/context": "self.version", - "open-telemetry/gen-otlp-protobuf": "self.version", - "open-telemetry/sdk": "self.version", - "open-telemetry/sdk-contrib": "self.version", - "open-telemetry/sem-conv": "self.version" + "open-telemetry/api": "1.0.x-dev", + "open-telemetry/context": "1.0.x-dev", + "open-telemetry/exporter-otlp": "1.0.x-dev", + "open-telemetry/exporter-zipkin": "1.0.x-dev", + "open-telemetry/extension-propagator-b3": "1.0.x-dev", + "open-telemetry/extension-propagator-jaeger": "0.0.2", + "open-telemetry/gen-otlp-protobuf": "1.0.x-dev", + "open-telemetry/sdk": "1.0.x-dev", + "open-telemetry/sdk-configuration": "0.1.x-dev", + "open-telemetry/sdk-contrib": "1.0.x-dev", + "open-telemetry/sem-conv": "1.0.x-dev" }, "autoload": { "psr-4": { @@ -108,5 +116,37 @@ "ext-gmp": "To support unlimited number of synchronous metric readers", "ext-grpc": "To use the OTLP GRPC Exporter", "ext-protobuf": "For more performant protobuf/grpc exporting" + }, + "extra": { + "spi": { + "Nevay\\OTelSDK\\Configuration\\ComponentProvider": [ + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Propagator\\TextMapPropagatorB3", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Propagator\\TextMapPropagatorB3Multi", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Propagator\\TextMapPropagatorBaggage", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Propagator\\TextMapPropagatorComposite", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Propagator\\TextMapPropagatorJaeger", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Propagator\\TextMapPropagatorTraceContext", + + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SamplerAlwaysOff", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SamplerAlwaysOn", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SamplerParentBased", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SamplerTraceIdRatioBased", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SpanExporterConsole", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SpanExporterOtlp", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SpanExporterZipkin", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SpanProcessorBatch", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SpanProcessorSimple", + + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Metrics\\AggregationResolverDefault", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Metrics\\MetricExporterConsole", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Metrics\\MetricExporterOtlp", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Metrics\\MetricReaderPeriodic", + + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordExporterConsole", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordExporterOtlp", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordProcessorBatch", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordProcessorSimple" + ] + } } } diff --git a/examples/load_config.php b/examples/load_config.php new file mode 100644 index 000000000..591f3f583 --- /dev/null +++ b/examples/load_config.php @@ -0,0 +1,29 @@ +create() + ->setAutoShutdown(true) + ->build(); + +$tracer = $sdk->getTracerProvider()->getTracer('demo'); +$meter = $sdk->getMeterProvider()->getMeter('demo'); +$logger = $sdk->getLoggerProvider()->getLogger('demo'); + +$tracer->spanBuilder('root')->startSpan()->end(); +$meter->createCounter('cnt')->add(1); + +$eventLogger = new EventLogger($logger, 'my-domain'); +$eventLogger->logEvent('foo', new LogRecord('hello, otel')); + +echo 'Finished!' . PHP_EOL; diff --git a/examples/load_config.yaml b/examples/load_config.yaml new file mode 100644 index 000000000..9e1901de8 --- /dev/null +++ b/examples/load_config.yaml @@ -0,0 +1,44 @@ +file_format: '0.1' + +resource: + attributes: + service.name: opentelemetry-demo + +propagators: + composite: [ tracecontext, baggage ] + +exporters: + otlp: &otlp-exporter + protocol: http/protobuf + endpoint: http://collector:4318 + +tracer_provider: + sampler: + parent_based: + root: + always_on: {} + processors: + - simple: + exporter: + console: {} + - batch: + exporter: + otlp: *otlp-exporter +meter_provider: + readers: + - periodic: + exporter: + console: {} + - periodic: + exporter: + otlp: + <<: *otlp-exporter + temporality_preference: lowmemory +logger_provider: + processors: + - simple: + exporter: + console: {} + - batch: + exporter: + otlp: *otlp-exporter diff --git a/examples/load_config_env.php b/examples/load_config_env.php new file mode 100644 index 000000000..6fa447949 --- /dev/null +++ b/examples/load_config_env.php @@ -0,0 +1,34 @@ +create() + ->setAutoShutdown(true) + ->build(); + +$tracer = $sdk->getTracerProvider()->getTracer('demo'); +$meter = $sdk->getMeterProvider()->getMeter('demo'); +$logger = $sdk->getLoggerProvider()->getLogger('demo'); + +$tracer->spanBuilder('root')->startSpan()->end(); +$meter->createCounter('cnt')->add(1); + +$eventLogger = new EventLogger($logger, 'my-domain'); +$eventLogger->logEvent('foo', new LogRecord('hello, otel')); + +echo 'Finished!' . PHP_EOL; diff --git a/examples/load_config_env.yaml b/examples/load_config_env.yaml new file mode 100644 index 000000000..b62262d29 --- /dev/null +++ b/examples/load_config_env.yaml @@ -0,0 +1,49 @@ +file_format: '0.1' + +disabled: ${OTEL_SDK_DISABLED} + +resource: + attributes: + service.name: ${OTEL_SERVICE_NAME} + +propagators: + composite: [ tracecontext, baggage ] + +attribute_limits: + attribute_value_length_limit: ${OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT} + attribute_count_limit: ${OTEL_ATTRIBUTE_COUNT_LIMIT} + +exporters: + otlp: &otlp-exporter + protocol: ${OTEL_EXPORTER_OTLP_PROTOCOL} + endpoint: ${OTEL_EXPORTER_OTLP_ENDPOINT} + certificate: ${OTEL_EXPORTER_OTLP_CERTIFICATE} + client_key: ${OTEL_EXPORTER_OTLP_CLIENT_KEY} + client_certificate: ${OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE} + compression: ${OTEL_EXPORTER_OTLP_COMPRESSION} + timeout: ${OTEL_EXPORTER_OTLP_TIMEOUT} + +tracer_provider: + sampler: + parent_based: + root: + trace_id_ratio_based: + ratio: ${OTEL_TRACES_SAMPLER_ARG} + limits: + attribute_value_length_limit: ${OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT} + attribute_count_limit: ${OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT} + event_count_limit: ${OTEL_SPAN_EVENT_COUNT_LIMIT} + link_count_limit: ${OTEL_SPAN_LINK_COUNT_LIMIT} + event_attribute_count_limit: ${OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT} + link_attribute_count_limit: ${OTEL_LINK_ATTRIBUTE_COUNT_LIMIT} + processors: + - simple: + exporter: + console: {} + - batch: + schedule_delay: ${OTEL_BSP_SCHEDULE_DELAY} + export_timeout: ${OTEL_BSP_EXPORT_TIMEOUT} + max_queue_size: ${OTEL_BSP_MAX_QUEUE_SIZE} + max_export_batch_size: ${OTEL_BSP_MAX_EXPORT_BATCH_SIZE} + exporter: + otlp: *otlp-exporter diff --git a/phpstan.neon.dist b/phpstan.neon.dist index fc15014fb..48d227e80 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -32,4 +32,8 @@ parameters: - message: "#Call to an undefined method .*:expects.*#" paths: - - tests \ No newline at end of file + - tests + - + message: "#Call to an undefined method Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\NodeParentInterface::.*#" + paths: + - src/Config/SDK diff --git a/psalm.xml.dist b/psalm.xml.dist index b6f04150f..905e3b7d9 100644 --- a/psalm.xml.dist +++ b/psalm.xml.dist @@ -23,13 +23,33 @@ - + - - + + - + - + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Config/SDK/ComponentProvider/Logs/LogRecordExporterConsole.php b/src/Config/SDK/ComponentProvider/Logs/LogRecordExporterConsole.php new file mode 100644 index 000000000..a978516c5 --- /dev/null +++ b/src/Config/SDK/ComponentProvider/Logs/LogRecordExporterConsole.php @@ -0,0 +1,36 @@ + + */ +final class LogRecordExporterConsole implements ComponentProvider +{ + + /** + * @param array{} $properties + */ + public function createPlugin(array $properties, Context $context): LogRecordExporterInterface + { + return new ConsoleExporter(Registry::transportFactory('stream')->create( + endpoint: 'php://stdout', + contentType: 'application/json', + )); + } + + public function getConfig(ComponentProviderRegistry $registry): ArrayNodeDefinition + { + return new ArrayNodeDefinition('console'); + } +} diff --git a/src/Config/SDK/ComponentProvider/Logs/LogRecordExporterOtlp.php b/src/Config/SDK/ComponentProvider/Logs/LogRecordExporterOtlp.php new file mode 100644 index 000000000..2be5fd161 --- /dev/null +++ b/src/Config/SDK/ComponentProvider/Logs/LogRecordExporterOtlp.php @@ -0,0 +1,75 @@ + + */ +#[PackageDependency('open-telemetry/exporter-otlp', '^1.0.5')] +final class LogRecordExporterOtlp implements ComponentProvider +{ + + /** + * @param array{ + * protocol: 'http/protobuf'|'http/json'|'grpc', + * endpoint: string, + * certificate: ?string, + * client_key: ?string, + * client_certificate: ?string, + * headers: array, + * compression: 'gzip'|null, + * timeout: int<0, max>, + * } $properties + */ + public function createPlugin(array $properties, Context $context): LogRecordExporterInterface + { + $protocol = $properties['protocol']; + + return new LogsExporter(Registry::transportFactory($protocol)->create( + endpoint: $properties['endpoint'] . OtlpUtil::path(Signals::LOGS, $protocol), + contentType: Protocols::contentType($protocol), + headers: $properties['headers'], + compression: $properties['compression'], + timeout: $properties['timeout'], + cacert: $properties['certificate'], + cert: $properties['client_certificate'], + key: $properties['client_certificate'], + )); + } + + public function getConfig(ComponentProviderRegistry $registry): ArrayNodeDefinition + { + $node = new ArrayNodeDefinition('otlp'); + $node + ->children() + ->enumNode('protocol')->isRequired()->values(['http/protobuf', 'http/json', 'grpc'])->end() + ->scalarNode('endpoint')->isRequired()->validate()->always(Validation::ensureString())->end()->end() + ->scalarNode('certificate')->defaultNull()->validate()->always(Validation::ensureString())->end()->end() + ->scalarNode('client_key')->defaultNull()->validate()->always(Validation::ensureString())->end()->end() + ->scalarNode('client_certificate')->defaultNull()->validate()->always(Validation::ensureString())->end()->end() + ->arrayNode('headers') + ->scalarPrototype()->end() + ->end() + ->enumNode('compression')->values(['gzip'])->defaultNull()->end() + ->integerNode('timeout')->min(0)->defaultValue(10)->end() + ->end() + ; + + return $node; + } +} diff --git a/src/Config/SDK/ComponentProvider/Logs/LogRecordProcessorBatch.php b/src/Config/SDK/ComponentProvider/Logs/LogRecordProcessorBatch.php new file mode 100644 index 000000000..712ca871b --- /dev/null +++ b/src/Config/SDK/ComponentProvider/Logs/LogRecordProcessorBatch.php @@ -0,0 +1,60 @@ + + */ +final class LogRecordProcessorBatch implements ComponentProvider +{ + + /** + * @param array{ + * schedule_delay: int<0, max>, + * export_timeout: int<0, max>, + * max_queue_size: int<0, max>, + * max_export_batch_size: int<0, max>, + * exporter: ComponentPlugin, + * } $properties + */ + public function createPlugin(array $properties, Context $context): LogRecordProcessorInterface + { + return new BatchLogRecordProcessor( + exporter: $properties['exporter']->create($context), + clock: ClockFactory::getDefault(), + maxQueueSize: $properties['max_queue_size'], + scheduledDelayMillis: $properties['schedule_delay'], + exportTimeoutMillis: $properties['export_timeout'], + maxExportBatchSize: $properties['max_export_batch_size'], + meterProvider: $context->meterProvider, + ); + } + + public function getConfig(ComponentProviderRegistry $registry): ArrayNodeDefinition + { + $node = new ArrayNodeDefinition('batch'); + $node + ->children() + ->integerNode('schedule_delay')->min(0)->defaultValue(5000)->end() + ->integerNode('export_timeout')->min(0)->defaultValue(30000)->end() + ->integerNode('max_queue_size')->min(0)->defaultValue(2048)->end() + ->integerNode('max_export_batch_size')->min(0)->defaultValue(512)->end() + ->append($registry->component('exporter', LogRecordExporterInterface::class)->isRequired()) + ->end() + ; + + return $node; + } +} diff --git a/src/Config/SDK/ComponentProvider/Logs/LogRecordProcessorSimple.php b/src/Config/SDK/ComponentProvider/Logs/LogRecordProcessorSimple.php new file mode 100644 index 000000000..edbaaa552 --- /dev/null +++ b/src/Config/SDK/ComponentProvider/Logs/LogRecordProcessorSimple.php @@ -0,0 +1,45 @@ + + */ +final class LogRecordProcessorSimple implements ComponentProvider +{ + + /** + * @param array{ + * exporter: ComponentPlugin, + * } $properties + */ + public function createPlugin(array $properties, Context $context): LogRecordProcessorInterface + { + return new SimpleLogRecordProcessor( + exporter: $properties['exporter']->create($context), + ); + } + + public function getConfig(ComponentProviderRegistry $registry): ArrayNodeDefinition + { + $node = new ArrayNodeDefinition('simple'); + $node + ->children() + ->append($registry->component('exporter', LogRecordExporterInterface::class)->isRequired()) + ->end() + ; + + return $node; + } +} diff --git a/src/Config/SDK/ComponentProvider/Metrics/AggregationResolverDefault.php b/src/Config/SDK/ComponentProvider/Metrics/AggregationResolverDefault.php new file mode 100644 index 000000000..d96cf3040 --- /dev/null +++ b/src/Config/SDK/ComponentProvider/Metrics/AggregationResolverDefault.php @@ -0,0 +1,35 @@ + + */ +final class AggregationResolverDefault implements ComponentProvider +{ + + /** + * @param array{} $properties + */ + public function createPlugin(array $properties, Context $context): DefaultAggregationProviderInterface + { + // TODO Implement proper aggregation providers (default, drop, explicit_bucket_histogram, last_value, sum) to handle advisory + return new class() implements DefaultAggregationProviderInterface { + use DefaultAggregationProviderTrait; + }; + } + + public function getConfig(ComponentProviderRegistry $registry): ArrayNodeDefinition + { + return new ArrayNodeDefinition('default'); + } +} diff --git a/src/Config/SDK/ComponentProvider/Metrics/MetricExporterConsole.php b/src/Config/SDK/ComponentProvider/Metrics/MetricExporterConsole.php new file mode 100644 index 000000000..6fba58cc5 --- /dev/null +++ b/src/Config/SDK/ComponentProvider/Metrics/MetricExporterConsole.php @@ -0,0 +1,32 @@ + + */ +final class MetricExporterConsole implements ComponentProvider +{ + + /** + * @param array{} $properties + */ + public function createPlugin(array $properties, Context $context): MetricExporterInterface + { + return new ConsoleMetricExporter(); + } + + public function getConfig(ComponentProviderRegistry $registry): ArrayNodeDefinition + { + return new ArrayNodeDefinition('console'); + } +} diff --git a/src/Config/SDK/ComponentProvider/Metrics/MetricExporterOtlp.php b/src/Config/SDK/ComponentProvider/Metrics/MetricExporterOtlp.php new file mode 100644 index 000000000..5547de09c --- /dev/null +++ b/src/Config/SDK/ComponentProvider/Metrics/MetricExporterOtlp.php @@ -0,0 +1,92 @@ + + */ +#[PackageDependency('open-telemetry/exporter-otlp', '^1.0.5')] +final class MetricExporterOtlp implements ComponentProvider +{ + + /** + * @param array{ + * protocol: 'http/protobuf'|'http/json'|'grpc', + * endpoint: string, + * certificate: ?string, + * client_key: ?string, + * client_certificate: ?string, + * headers: array, + * compression: 'gzip'|null, + * timeout: int<0, max>, + * temporality_preference: 'cumulative'|'delta'|'lowmemory', + * default_histogram_aggregation: 'explicit_bucket_histogram', + * } $properties + */ + public function createPlugin(array $properties, Context $context): MetricExporterInterface + { + $protocol = $properties['protocol']; + + $temporality = match ($properties['temporality_preference']) { + 'cumulative' => Temporality::CUMULATIVE, + 'delta' => Temporality::DELTA, + 'lowmemory' => null, + }; + + return new MetricExporter(Registry::transportFactory($protocol)->create( + endpoint: $properties['endpoint'] . OtlpUtil::path(Signals::METRICS, $protocol), + contentType: Protocols::contentType($protocol), + headers: $properties['headers'], + compression: $properties['compression'], + timeout: $properties['timeout'], + cacert: $properties['certificate'], + cert: $properties['client_certificate'], + key: $properties['client_certificate'], + ), $temporality); + } + + public function getConfig(ComponentProviderRegistry $registry): ArrayNodeDefinition + { + $node = new ArrayNodeDefinition('otlp'); + $node + ->children() + ->enumNode('protocol')->isRequired()->values(['http/protobuf', 'http/json', 'grpc'])->end() + ->scalarNode('endpoint')->isRequired()->validate()->always(Validation::ensureString())->end()->end() + ->scalarNode('certificate')->defaultNull()->validate()->always(Validation::ensureString())->end()->end() + ->scalarNode('client_key')->defaultNull()->validate()->always(Validation::ensureString())->end()->end() + ->scalarNode('client_certificate')->defaultNull()->validate()->always(Validation::ensureString())->end()->end() + ->arrayNode('headers') + ->scalarPrototype()->end() + ->end() + ->enumNode('compression')->values(['gzip'])->defaultNull()->validate()->always(Validation::ensureString())->end()->end() + ->integerNode('timeout')->min(0)->defaultValue(10)->end() + ->enumNode('temporality_preference') + ->values(['cumulative', 'delta', 'lowmemory']) + ->defaultValue('cumulative') + ->end() + ->enumNode('default_histogram_aggregation') + ->values(['explicit_bucket_histogram']) + ->defaultValue('explicit_bucket_histogram') + ->end() + ->end() + ; + + return $node; + } +} diff --git a/src/Config/SDK/ComponentProvider/Metrics/MetricReaderPeriodic.php b/src/Config/SDK/ComponentProvider/Metrics/MetricReaderPeriodic.php new file mode 100644 index 000000000..0e77e2008 --- /dev/null +++ b/src/Config/SDK/ComponentProvider/Metrics/MetricReaderPeriodic.php @@ -0,0 +1,49 @@ + + */ +final class MetricReaderPeriodic implements ComponentProvider +{ + + /** + * @param array{ + * interval: int<0, max>, + * timeout: int<0, max>, + * exporter: ComponentPlugin, + * } $properties + */ + public function createPlugin(array $properties, Context $context): MetricReaderInterface + { + return new ExportingReader( + exporter: $properties['exporter']->create($context), + ); + } + + public function getConfig(ComponentProviderRegistry $registry): ArrayNodeDefinition + { + $node = new ArrayNodeDefinition('periodic'); + $node + ->children() + ->integerNode('interval')->min(0)->defaultValue(5000)->end() + ->integerNode('timeout')->min(0)->defaultValue(30000)->end() + ->append($registry->component('exporter', MetricExporterInterface::class)->isRequired()) + ->end() + ; + + return $node; + } +} diff --git a/src/Config/SDK/ComponentProvider/OpenTelemetrySdk.php b/src/Config/SDK/ComponentProvider/OpenTelemetrySdk.php new file mode 100644 index 000000000..c8a11fbe1 --- /dev/null +++ b/src/Config/SDK/ComponentProvider/OpenTelemetrySdk.php @@ -0,0 +1,400 @@ + + */ +final class OpenTelemetrySdk implements ComponentProvider +{ + + /** + * @param array{ + * file_format: '0.1', + * disabled: bool, + * resource: array{ + * attributes: array, + * schema_url: ?string, + * }, + * attribute_limits: array{ + * attribute_value_length_limit: ?int<0, max>, + * attribute_count_limit: int<0, max>, + * }, + * propagator: ?ComponentPlugin, + * tracer_provider: array{ + * limits: array{ + * attribute_value_length_limit: ?int<0, max>, + * attribute_count_limit: ?int<0, max>, + * event_count_limit: int<0, max>, + * link_count_limit: int<0, max>, + * event_attribute_count_limit: ?int<0, max>, + * link_attribute_count_limit: ?int<0, max>, + * }, + * sampler: ?ComponentPlugin, + * processors: list>, + * }, + * meter_provider: array{ + * views: list, + * aggregation: ?ComponentPlugin, + * }, + * selector: array{ + * instrument_type: 'counter'|'histogram'|'observable_counter'|'observable_gauge'|'observable_up_down_counter'|'up_down_counter'|null, + * instrument_name: ?non-empty-string, + * unit: ?string, + * meter_name: ?string, + * meter_version: ?string, + * meter_schema_url: ?string, + * }, + * }>, + * readers: list>, + * }, + * logger_provider: array{ + * limits: array{ + * attribute_value_length_limit: ?int<0, max>, + * attribute_count_limit: ?int<0, max>, + * }, + * processors: list>, + * }, + * } $properties + */ + public function createPlugin(array $properties, Context $context): SdkBuilder + { + $sdkBuilder = new SdkBuilder(); + + $propagator = $properties['propagator']?->create($context) ?? NoopTextMapPropagator::getInstance(); + $sdkBuilder->setPropagator($propagator); + + if ($properties['disabled']) { + return $sdkBuilder; + } + + $resource = ResourceInfoFactory::defaultResource() + ->merge(ResourceInfo::create( + attributes: Attributes::create($properties['resource']['attributes']), + schemaUrl: $properties['resource']['schema_url'], + )); + + $spanProcessors = []; + foreach ($properties['tracer_provider']['processors'] as $processor) { + $spanProcessors[] = $processor->create($context); + } + + // + + $tracerProvider = new TracerProvider( + spanProcessors: $spanProcessors, + sampler: $properties['tracer_provider']['sampler']?->create($context) ?? new ParentBased(new AlwaysOnSampler()), + resource: $resource, + spanLimits: new SpanLimits( + attributesFactory: Attributes::factory( + attributeCountLimit: $properties['tracer_provider']['limits']['attribute_count_limit'] + ?? $properties['attribute_limits']['attribute_count_limit'], + attributeValueLengthLimit: $properties['tracer_provider']['limits']['attribute_value_length_limit'] + ?? $properties['attribute_limits']['attribute_value_length_limit'], + ), + eventAttributesFactory: Attributes::factory( + attributeCountLimit: $properties['tracer_provider']['limits']['event_attribute_count_limit'] + ?? $properties['tracer_provider']['limits']['attribute_count_limit'] + ?? $properties['attribute_limits']['attribute_count_limit'], + attributeValueLengthLimit: $properties['tracer_provider']['limits']['attribute_value_length_limit'] + ?? $properties['attribute_limits']['attribute_value_length_limit'], + ), + linkAttributesFactory: Attributes::factory( + attributeCountLimit: $properties['tracer_provider']['limits']['link_attribute_count_limit'] + ?? $properties['tracer_provider']['limits']['attribute_count_limit'] + ?? $properties['attribute_limits']['attribute_count_limit'], + attributeValueLengthLimit: $properties['tracer_provider']['limits']['attribute_value_length_limit'] + ?? $properties['attribute_limits']['attribute_value_length_limit'], + ), + eventCountLimit: $properties['tracer_provider']['limits']['event_count_limit'], + linkCountLimit: $properties['tracer_provider']['limits']['link_count_limit'], + ), + ); + + // + + // + + $metricReaders = []; + foreach ($properties['meter_provider']['readers'] as $reader) { + $metricReaders[] = $reader->create($context); + } + + $viewRegistry = new CriteriaViewRegistry(); + foreach ($properties['meter_provider']['views'] as $view) { + $criteria = []; + if (isset($view['selector']['instrument_type'])) { + $criteria[] = new InstrumentTypeCriteria(match ($view['selector']['instrument_type']) { + 'counter' => InstrumentType::COUNTER, + 'histogram' => InstrumentType::HISTOGRAM, + 'observable_counter' => InstrumentType::ASYNCHRONOUS_COUNTER, + 'observable_gauge' => InstrumentType::ASYNCHRONOUS_GAUGE, + 'observable_up_down_counter' => InstrumentType::ASYNCHRONOUS_UP_DOWN_COUNTER, + 'up_down_counter' => InstrumentType::UP_DOWN_COUNTER, + }); + } + if (isset($view['selector']['instrument_name'])) { + $criteria[] = new InstrumentNameCriteria($view['selector']['instrument_name']); + } + if (isset($view['selector']['unit'])) { + // TODO Add unit criteria + } + if (isset($view['selector']['meter_name'])) { + $criteria[] = new InstrumentationScopeNameCriteria($view['selector']['meter_name']); + } + if (isset($view['selector']['meter_version'])) { + $criteria[] = new InstrumentationScopeVersionCriteria($view['selector']['meter_version']); + } + if (isset($view['selector']['meter_schema_url'])) { + $criteria[] = new InstrumentationScopeSchemaUrlCriteria($view['selector']['meter_schema_url']); + } + + $viewTemplate = ViewTemplate::create(); + if (isset($view['stream']['name'])) { + $viewTemplate = $viewTemplate->withName($view['stream']['name']); + } + if (isset($view['stream']['description'])) { + $viewTemplate = $viewTemplate->withDescription($view['stream']['description']); + } + if ($view['stream']['attribute_keys']) { + $viewTemplate = $viewTemplate->withAttributeKeys($view['stream']['attribute_keys']); + } + if (isset($view['stream']['aggregation'])) { + // TODO Add support for aggregation providers in views to allow usage of advisory + } + + $viewRegistry->register(new AllCriteria($criteria), $viewTemplate); + } + + /** @psalm-suppress InvalidArgument TODO update metric reader interface */ + $meterProvider = new MeterProvider( + contextStorage: null, + resource: $resource, + clock: ClockFactory::getDefault(), + attributesFactory: Attributes::factory(), + instrumentationScopeFactory: new InstrumentationScopeFactory(Attributes::factory()), + metricReaders: $metricReaders, // @phpstan-ignore-line + viewRegistry: $viewRegistry, + exemplarFilter: null, + stalenessHandlerFactory: new NoopStalenessHandlerFactory(), + ); + + // + + // + + $logRecordProcessors = []; + foreach ($properties['logger_provider']['processors'] as $processor) { + $logRecordProcessors[] = $processor->create($context); + } + + // TODO Allow injecting log record attributes factory + $loggerProvider = new LoggerProvider( + processor: new MultiLogRecordProcessor($logRecordProcessors), + instrumentationScopeFactory: new InstrumentationScopeFactory(Attributes::factory()), + resource: $resource, + ); + + // + + $sdkBuilder->setTracerProvider($tracerProvider); + $sdkBuilder->setMeterProvider($meterProvider); + $sdkBuilder->setLoggerProvider($loggerProvider); + + return $sdkBuilder; + } + + public function getConfig(ComponentProviderRegistry $registry): ArrayNodeDefinition + { + $node = new ArrayNodeDefinition('open_telemetry'); + $node + ->addDefaultsIfNotSet() + ->ignoreExtraKeys() + ->children() + ->scalarNode('file_format') + ->isRequired() + ->example('0.1') + ->validate()->always(Validation::ensureString())->end() + ->validate()->ifNotInArray(['0.1'])->thenInvalid('unsupported version')->end() + ->end() + ->booleanNode('disabled')->defaultFalse()->end() + ->append($this->getResourceConfig()) + ->append($this->getAttributeLimitsConfig()) + ->append($registry->component('propagator', TextMapPropagatorInterface::class)) + ->append($this->getTracerProviderConfig($registry)) + ->append($this->getMeterProviderConfig($registry)) + ->append($this->getLoggerProviderConfig($registry)) + ->end(); + + return $node; + } + + private function getResourceConfig(): ArrayNodeDefinition + { + $node = new ArrayNodeDefinition('resource'); + $node + ->addDefaultsIfNotSet() + ->children() + ->arrayNode('attributes') + ->variablePrototype()->end() + ->end() + ->scalarNode('schema_url')->defaultNull()->validate()->always(Validation::ensureString())->end()->end() + ->end(); + + return $node; + } + + private function getAttributeLimitsConfig(): ArrayNodeDefinition + { + $node = new ArrayNodeDefinition('attribute_limits'); + $node + ->addDefaultsIfNotSet() + ->children() + ->integerNode('attribute_value_length_limit')->min(0)->defaultNull()->end() + ->integerNode('attribute_count_limit')->min(0)->defaultValue(128)->end() + ->end(); + + return $node; + } + + private function getTracerProviderConfig(ComponentProviderRegistry $registry): ArrayNodeDefinition + { + $node = new ArrayNodeDefinition('tracer_provider'); + $node + ->addDefaultsIfNotSet() + ->children() + ->arrayNode('limits') + ->addDefaultsIfNotSet() + ->children() + ->integerNode('attribute_value_length_limit')->min(0)->defaultNull()->end() + ->integerNode('attribute_count_limit')->min(0)->defaultNull()->end() + ->integerNode('event_count_limit')->min(0)->defaultValue(128)->end() + ->integerNode('link_count_limit')->min(0)->defaultValue(128)->end() + ->integerNode('event_attribute_count_limit')->min(0)->defaultNull()->end() + ->integerNode('link_attribute_count_limit')->min(0)->defaultNull()->end() + ->end() + ->end() + ->append($registry->component('sampler', SamplerInterface::class)) + ->append($registry->componentList('processors', SpanProcessorInterface::class)) + ->end() + ; + + return $node; + } + + private function getMeterProviderConfig(ComponentProviderRegistry $registry): ArrayNodeDefinition + { + $node = new ArrayNodeDefinition('meter_provider'); + $node + ->addDefaultsIfNotSet() + ->children() + ->arrayNode('views') + ->arrayPrototype() + ->children() + ->arrayNode('stream') + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('name')->defaultNull()->validate()->always(Validation::ensureString())->end()->end() + ->scalarNode('description')->defaultNull()->validate()->always(Validation::ensureString())->end()->end() + ->arrayNode('attribute_keys') + ->scalarPrototype()->validate()->always(Validation::ensureString())->end()->end() + ->end() + ->append($registry->component('aggregation', DefaultAggregationProviderInterface::class)) + ->end() + ->end() + ->arrayNode('selector') + ->addDefaultsIfNotSet() + ->children() + ->enumNode('instrument_type') + ->values([ + 'counter', + 'histogram', + 'observable_counter', + 'observable_gauge', + 'observable_up_down_counter', + 'up_down_counter', + ]) + ->defaultNull() + ->end() + ->scalarNode('instrument_name')->defaultNull()->validate()->always(Validation::ensureString())->end()->cannotBeEmpty()->end() + ->scalarNode('unit')->defaultNull()->validate()->always(Validation::ensureString())->end()->end() + ->scalarNode('meter_name')->defaultNull()->validate()->always(Validation::ensureString())->end()->end() + ->scalarNode('meter_version')->defaultNull()->validate()->always(Validation::ensureString())->end()->end() + ->scalarNode('meter_schema_url')->defaultNull()->validate()->always(Validation::ensureString())->end()->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->append($registry->componentList('readers', MetricReaderInterface::class)) + ->end() + ; + + return $node; + } + + private function getLoggerProviderConfig(ComponentProviderRegistry $registry): ArrayNodeDefinition + { + $node = new ArrayNodeDefinition('logger_provider'); + $node + ->addDefaultsIfNotSet() + ->children() + ->arrayNode('limits') + ->addDefaultsIfNotSet() + ->children() + ->integerNode('attribute_value_length_limit')->min(0)->defaultNull()->end() + ->integerNode('attribute_count_limit')->min(0)->defaultNull()->end() + ->end() + ->end() + ->append($registry->componentList('processors', LogRecordProcessorInterface::class)) + ->end() + ; + + return $node; + } +} diff --git a/src/Config/SDK/ComponentProvider/Propagator/TextMapPropagatorB3.php b/src/Config/SDK/ComponentProvider/Propagator/TextMapPropagatorB3.php new file mode 100644 index 000000000..9640f1e32 --- /dev/null +++ b/src/Config/SDK/ComponentProvider/Propagator/TextMapPropagatorB3.php @@ -0,0 +1,34 @@ + + */ +#[PackageDependency('open-telemetry/extension-propagator-b3', '^1.0.1')] +final class TextMapPropagatorB3 implements ComponentProvider +{ + + /** + * @param array{} $properties + */ + public function createPlugin(array $properties, Context $context): TextMapPropagatorInterface + { + return B3Propagator::getB3SingleHeaderInstance(); + } + + public function getConfig(ComponentProviderRegistry $registry): ArrayNodeDefinition + { + return new ArrayNodeDefinition('b3'); + } +} diff --git a/src/Config/SDK/ComponentProvider/Propagator/TextMapPropagatorB3Multi.php b/src/Config/SDK/ComponentProvider/Propagator/TextMapPropagatorB3Multi.php new file mode 100644 index 000000000..787e99911 --- /dev/null +++ b/src/Config/SDK/ComponentProvider/Propagator/TextMapPropagatorB3Multi.php @@ -0,0 +1,34 @@ + + */ +#[PackageDependency('open-telemetry/extension-propagator-b3', '^1.0.1')] +final class TextMapPropagatorB3Multi implements ComponentProvider +{ + + /** + * @param array{} $properties + */ + public function createPlugin(array $properties, Context $context): TextMapPropagatorInterface + { + return B3Propagator::getB3MultiHeaderInstance(); + } + + public function getConfig(ComponentProviderRegistry $registry): ArrayNodeDefinition + { + return new ArrayNodeDefinition('b3multi'); + } +} diff --git a/src/Config/SDK/ComponentProvider/Propagator/TextMapPropagatorBaggage.php b/src/Config/SDK/ComponentProvider/Propagator/TextMapPropagatorBaggage.php new file mode 100644 index 000000000..ecfde7fa1 --- /dev/null +++ b/src/Config/SDK/ComponentProvider/Propagator/TextMapPropagatorBaggage.php @@ -0,0 +1,32 @@ + + */ +final class TextMapPropagatorBaggage implements ComponentProvider +{ + + /** + * @param array{} $properties + */ + public function createPlugin(array $properties, Context $context): TextMapPropagatorInterface + { + return BaggagePropagator::getInstance(); + } + + public function getConfig(ComponentProviderRegistry $registry): ArrayNodeDefinition + { + return new ArrayNodeDefinition('baggage'); + } +} diff --git a/src/Config/SDK/ComponentProvider/Propagator/TextMapPropagatorComposite.php b/src/Config/SDK/ComponentProvider/Propagator/TextMapPropagatorComposite.php new file mode 100644 index 000000000..6eba5391a --- /dev/null +++ b/src/Config/SDK/ComponentProvider/Propagator/TextMapPropagatorComposite.php @@ -0,0 +1,38 @@ + + */ +final class TextMapPropagatorComposite implements ComponentProvider +{ + + /** + * @param list> $properties + */ + public function createPlugin(array $properties, Context $context): TextMapPropagatorInterface + { + $propagators = []; + foreach ($properties as $plugin) { + $propagators[] = $plugin->create($context); + } + + return new MultiTextMapPropagator($propagators); + } + + public function getConfig(ComponentProviderRegistry $registry): ArrayNodeDefinition + { + return $registry->componentNames('composite', TextMapPropagatorInterface::class); + } +} diff --git a/src/Config/SDK/ComponentProvider/Propagator/TextMapPropagatorJaeger.php b/src/Config/SDK/ComponentProvider/Propagator/TextMapPropagatorJaeger.php new file mode 100644 index 000000000..6d8e2f4a7 --- /dev/null +++ b/src/Config/SDK/ComponentProvider/Propagator/TextMapPropagatorJaeger.php @@ -0,0 +1,34 @@ + + */ +#[PackageDependency('open-telemetry/extension-propagator-jaeger', '^0.0.2')] +final class TextMapPropagatorJaeger implements ComponentProvider +{ + + /** + * @param array{} $properties + */ + public function createPlugin(array $properties, Context $context): TextMapPropagatorInterface + { + return JaegerPropagator::getInstance(); + } + + public function getConfig(ComponentProviderRegistry $registry): ArrayNodeDefinition + { + return new ArrayNodeDefinition('jaeger'); + } +} diff --git a/src/Config/SDK/ComponentProvider/Propagator/TextMapPropagatorTraceContext.php b/src/Config/SDK/ComponentProvider/Propagator/TextMapPropagatorTraceContext.php new file mode 100644 index 000000000..0913be336 --- /dev/null +++ b/src/Config/SDK/ComponentProvider/Propagator/TextMapPropagatorTraceContext.php @@ -0,0 +1,32 @@ + + */ +final class TextMapPropagatorTraceContext implements ComponentProvider +{ + + /** + * @param array{} $properties + */ + public function createPlugin(array $properties, Context $context): TextMapPropagatorInterface + { + return TraceContextPropagator::getInstance(); + } + + public function getConfig(ComponentProviderRegistry $registry): ArrayNodeDefinition + { + return new ArrayNodeDefinition('tracecontext'); + } +} diff --git a/src/Config/SDK/ComponentProvider/Trace/SamplerAlwaysOff.php b/src/Config/SDK/ComponentProvider/Trace/SamplerAlwaysOff.php new file mode 100644 index 000000000..7da8a2d25 --- /dev/null +++ b/src/Config/SDK/ComponentProvider/Trace/SamplerAlwaysOff.php @@ -0,0 +1,32 @@ + + */ +final class SamplerAlwaysOff implements ComponentProvider +{ + + /** + * @param array{} $properties + */ + public function createPlugin(array $properties, Context $context): SamplerInterface + { + return new AlwaysOffSampler(); + } + + public function getConfig(ComponentProviderRegistry $registry): ArrayNodeDefinition + { + return new ArrayNodeDefinition('always_off'); + } +} diff --git a/src/Config/SDK/ComponentProvider/Trace/SamplerAlwaysOn.php b/src/Config/SDK/ComponentProvider/Trace/SamplerAlwaysOn.php new file mode 100644 index 000000000..15dbe61b6 --- /dev/null +++ b/src/Config/SDK/ComponentProvider/Trace/SamplerAlwaysOn.php @@ -0,0 +1,32 @@ + + */ +final class SamplerAlwaysOn implements ComponentProvider +{ + + /** + * @param array{} $properties + */ + public function createPlugin(array $properties, Context $context): SamplerInterface + { + return new AlwaysOnSampler(); + } + + public function getConfig(ComponentProviderRegistry $registry): ArrayNodeDefinition + { + return new ArrayNodeDefinition('always_on'); + } +} diff --git a/src/Config/SDK/ComponentProvider/Trace/SamplerParentBased.php b/src/Config/SDK/ComponentProvider/Trace/SamplerParentBased.php new file mode 100644 index 000000000..121282359 --- /dev/null +++ b/src/Config/SDK/ComponentProvider/Trace/SamplerParentBased.php @@ -0,0 +1,58 @@ + + */ +final class SamplerParentBased implements ComponentProvider +{ + + /** + * @param array{ + * root: ComponentPlugin, + * remote_parent_sampled: ?ComponentPlugin, + * remote_parent_not_sampled: ?ComponentPlugin, + * local_parent_sampled: ?ComponentPlugin, + * local_parent_not_sampled: ?ComponentPlugin, + * } $properties + */ + public function createPlugin(array $properties, Context $context): SamplerInterface + { + return new ParentBased( + root: $properties['root']->create($context), + remoteParentSampler: $properties['remote_parent_sampled']?->create($context) ?? new AlwaysOnSampler(), + remoteParentNotSampler: $properties['remote_parent_not_sampled']?->create($context) ?? new AlwaysOffSampler(), + localParentSampler: $properties['local_parent_sampled']?->create($context) ?? new AlwaysOnSampler(), + localParentNotSampler: $properties['local_parent_not_sampled']?->create($context) ?? new AlwaysOffSampler(), + ); + } + + public function getConfig(ComponentProviderRegistry $registry): ArrayNodeDefinition + { + $node = new ArrayNodeDefinition('parent_based'); + $node + ->children() + ->append($registry->component('root', SamplerInterface::class)->isRequired()) + ->append($registry->component('remote_parent_sampled', SamplerInterface::class)) + ->append($registry->component('remote_parent_not_sampled', SamplerInterface::class)) + ->append($registry->component('local_parent_sampled', SamplerInterface::class)) + ->append($registry->component('local_parent_not_sampled', SamplerInterface::class)) + ->end() + ; + + return $node; + } +} diff --git a/src/Config/SDK/ComponentProvider/Trace/SamplerTraceIdRatioBased.php b/src/Config/SDK/ComponentProvider/Trace/SamplerTraceIdRatioBased.php new file mode 100644 index 000000000..c66d203da --- /dev/null +++ b/src/Config/SDK/ComponentProvider/Trace/SamplerTraceIdRatioBased.php @@ -0,0 +1,43 @@ + + */ +final class SamplerTraceIdRatioBased implements ComponentProvider +{ + + /** + * @param array{ + * ratio: float, + * } $properties + */ + public function createPlugin(array $properties, Context $context): SamplerInterface + { + return new TraceIdRatioBasedSampler( + probability: $properties['ratio'], + ); + } + + public function getConfig(ComponentProviderRegistry $registry): ArrayNodeDefinition + { + $node = new ArrayNodeDefinition('trace_id_ratio_based'); + $node + ->children() + ->floatNode('ratio')->min(0)->max(1)->isRequired()->end() + ->end() + ; + + return $node; + } +} diff --git a/src/Config/SDK/ComponentProvider/Trace/SpanExporterConsole.php b/src/Config/SDK/ComponentProvider/Trace/SpanExporterConsole.php new file mode 100644 index 000000000..f36fe5258 --- /dev/null +++ b/src/Config/SDK/ComponentProvider/Trace/SpanExporterConsole.php @@ -0,0 +1,36 @@ + + */ +final class SpanExporterConsole implements ComponentProvider +{ + + /** + * @param array{} $properties + */ + public function createPlugin(array $properties, Context $context): SpanExporterInterface + { + return new ConsoleSpanExporter(Registry::transportFactory('stream')->create( + endpoint: 'php://stdout', + contentType: 'application/json', + )); + } + + public function getConfig(ComponentProviderRegistry $registry): ArrayNodeDefinition + { + return new ArrayNodeDefinition('console'); + } +} diff --git a/src/Config/SDK/ComponentProvider/Trace/SpanExporterOtlp.php b/src/Config/SDK/ComponentProvider/Trace/SpanExporterOtlp.php new file mode 100644 index 000000000..6166bd725 --- /dev/null +++ b/src/Config/SDK/ComponentProvider/Trace/SpanExporterOtlp.php @@ -0,0 +1,75 @@ + + */ +#[PackageDependency('open-telemetry/exporter-otlp', '^1.0.5')] +final class SpanExporterOtlp implements ComponentProvider +{ + + /** + * @param array{ + * protocol: 'http/protobuf'|'http/json'|'grpc', + * endpoint: string, + * certificate: ?string, + * client_key: ?string, + * client_certificate: ?string, + * headers: array, + * compression: 'gzip'|null, + * timeout: int<0, max>, + * } $properties + */ + public function createPlugin(array $properties, Context $context): SpanExporterInterface + { + $protocol = $properties['protocol']; + + return new SpanExporter(Registry::transportFactory($protocol)->create( + endpoint: $properties['endpoint'] . OtlpUtil::path(Signals::TRACE, $protocol), + contentType: Protocols::contentType($protocol), + headers: $properties['headers'], + compression: $properties['compression'], + timeout: $properties['timeout'], + cacert: $properties['certificate'], + cert: $properties['client_certificate'], + key: $properties['client_certificate'], + )); + } + + public function getConfig(ComponentProviderRegistry $registry): ArrayNodeDefinition + { + $node = new ArrayNodeDefinition('otlp'); + $node + ->children() + ->enumNode('protocol')->isRequired()->values(['http/protobuf', 'http/json', 'grpc'])->end() + ->scalarNode('endpoint')->isRequired()->validate()->always(Validation::ensureString())->end()->end() + ->scalarNode('certificate')->defaultNull()->validate()->always(Validation::ensureString())->end()->end() + ->scalarNode('client_key')->defaultNull()->validate()->always(Validation::ensureString())->end()->end() + ->scalarNode('client_certificate')->defaultNull()->validate()->always(Validation::ensureString())->end()->end() + ->arrayNode('headers') + ->scalarPrototype()->end() + ->end() + ->enumNode('compression')->values(['gzip'])->defaultNull()->end() + ->integerNode('timeout')->min(0)->defaultValue(10)->end() + ->end() + ; + + return $node; + } +} diff --git a/src/Config/SDK/ComponentProvider/Trace/SpanExporterZipkin.php b/src/Config/SDK/ComponentProvider/Trace/SpanExporterZipkin.php new file mode 100644 index 000000000..4e13f49ea --- /dev/null +++ b/src/Config/SDK/ComponentProvider/Trace/SpanExporterZipkin.php @@ -0,0 +1,51 @@ + + */ +#[PackageDependency('open-telemetry/exporter-zipkin', '^1.0')] +final class SpanExporterZipkin implements ComponentProvider +{ + + /** + * @param array{ + * endpoint: string, + * timeout: int<0, max>, + * } $properties + */ + public function createPlugin(array $properties, Context $context): SpanExporterInterface + { + return new Zipkin\Exporter(Registry::transportFactory('http')->create( + endpoint: $properties['endpoint'], + contentType: 'application/json', + timeout: $properties['timeout'], + )); + } + + public function getConfig(ComponentProviderRegistry $registry): ArrayNodeDefinition + { + $node = new ArrayNodeDefinition('zipkin'); + $node + ->children() + ->scalarNode('endpoint')->isRequired()->validate()->always(Validation::ensureString())->end()->end() + ->integerNode('timeout')->min(0)->defaultValue(10)->end() + ->end() + ; + + return $node; + } +} diff --git a/src/Config/SDK/ComponentProvider/Trace/SpanProcessorBatch.php b/src/Config/SDK/ComponentProvider/Trace/SpanProcessorBatch.php new file mode 100644 index 000000000..3688473f0 --- /dev/null +++ b/src/Config/SDK/ComponentProvider/Trace/SpanProcessorBatch.php @@ -0,0 +1,60 @@ + + */ +final class SpanProcessorBatch implements ComponentProvider +{ + + /** + * @param array{ + * schedule_delay: int<0, max>, + * export_timeout: int<0, max>, + * max_queue_size: int<0, max>, + * max_export_batch_size: int<0, max>, + * exporter: ComponentPlugin, + * } $properties + */ + public function createPlugin(array $properties, Context $context): SpanProcessorInterface + { + return new BatchSpanProcessor( + exporter: $properties['exporter']->create($context), + clock: ClockFactory::getDefault(), + maxQueueSize: $properties['max_queue_size'], + scheduledDelayMillis: $properties['schedule_delay'], + exportTimeoutMillis: $properties['export_timeout'], + maxExportBatchSize: $properties['max_export_batch_size'], + meterProvider: $context->meterProvider, + ); + } + + public function getConfig(ComponentProviderRegistry $registry): ArrayNodeDefinition + { + $node = new ArrayNodeDefinition('batch'); + $node + ->children() + ->integerNode('schedule_delay')->min(0)->defaultValue(5000)->end() + ->integerNode('export_timeout')->min(0)->defaultValue(30000)->end() + ->integerNode('max_queue_size')->min(0)->defaultValue(2048)->end() + ->integerNode('max_export_batch_size')->min(0)->defaultValue(512)->end() + ->append($registry->component('exporter', SpanExporterInterface::class)->isRequired()) + ->end() + ; + + return $node; + } +} diff --git a/src/Config/SDK/ComponentProvider/Trace/SpanProcessorSimple.php b/src/Config/SDK/ComponentProvider/Trace/SpanProcessorSimple.php new file mode 100644 index 000000000..ad15e00d4 --- /dev/null +++ b/src/Config/SDK/ComponentProvider/Trace/SpanProcessorSimple.php @@ -0,0 +1,45 @@ + + */ +final class SpanProcessorSimple implements ComponentProvider +{ + + /** + * @param array{ + * exporter: ComponentPlugin, + * } $properties + */ + public function createPlugin(array $properties, Context $context): SpanProcessorInterface + { + return new SimpleSpanProcessor( + exporter: $properties['exporter']->create($context), + ); + } + + public function getConfig(ComponentProviderRegistry $registry): ArrayNodeDefinition + { + $node = new ArrayNodeDefinition('simple'); + $node + ->children() + ->append($registry->component('exporter', SpanExporterInterface::class)->isRequired()) + ->end() + ; + + return $node; + } +} diff --git a/src/Config/SDK/Configuration.php b/src/Config/SDK/Configuration.php new file mode 100644 index 000000000..b06f596e7 --- /dev/null +++ b/src/Config/SDK/Configuration.php @@ -0,0 +1,61 @@ + $sdkPlugin + */ + private function __construct( + private readonly ComponentPlugin $sdkPlugin, + ) { + } + + public function create(Context $context = new Context()): SdkBuilder + { + return $this->sdkPlugin->create($context); + } + + /** + * @param string|list $file + */ + public static function parseFile( + string|array $file, + ?string $cacheFile = null, + bool $debug = true, + ): Configuration { + return new self(self::factory()->parseFile($file, $cacheFile, $debug)); + } + + /** + * @return ConfigurationFactory + */ + private static function factory(): ConfigurationFactory + { + static $factory; + + return $factory ??= new ConfigurationFactory( + ServiceLoader::load(ComponentProvider::class), + new OpenTelemetrySdk(), + new EnvSourceReader([ + new ServerEnvSource(), + new PhpIniEnvSource(), + ]), + ); + } +} diff --git a/src/Config/SDK/README.md b/src/Config/SDK/README.md new file mode 100644 index 000000000..022d2ce47 --- /dev/null +++ b/src/Config/SDK/README.md @@ -0,0 +1,34 @@ +# OpenTelemetry SDK configuration + +## Installation + +```shell +composer require open-telemetry/sdk-configuration +``` + +## Usage + +### Initialization from [configuration file](https://opentelemetry.io/docs/specs/otel/configuration/file-configuration/) + +```php +$configuration = Configuration::parseFile(__DIR__ . '/kitchen-sink.yaml'); +$sdkBuilder = $configuration->create(); +``` + +#### Performance considerations + +Parsing and processing the configuration is rather expensive. It is highly recommended to provide the `$cacheFile` +parameter when running in a shared-nothing setup. + +```php +$configuration = Configuration::parseFile( + __DIR__ . '/kitchen-sink.yaml', + __DIR__ . '/var/cache/opentelemetry.php', +); +$sdkBuilder = $configuration->create(); +``` + +## Contributing + +This repository is a read-only git subtree split. +To contribute, please see the main [OpenTelemetry PHP monorepo](https://github.com/open-telemetry/opentelemetry-php). diff --git a/src/Config/SDK/composer.json b/src/Config/SDK/composer.json new file mode 100644 index 000000000..7b6809c3f --- /dev/null +++ b/src/Config/SDK/composer.json @@ -0,0 +1,65 @@ +{ + "name": "open-telemetry/sdk-configuration", + "description": "SDK configuration for OpenTelemetry PHP.", + "keywords": ["opentelemetry", "otel", "sdk", "configuration"], + "type": "library", + "support": { + "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", + "source": "https://github.com/open-telemetry/opentelemetry-php", + "docs": "https://opentelemetry.io/docs/php", + "chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V" + }, + "license": "Apache-2.0", + "authors": [ + { + "name": "opentelemetry-php contributors", + "homepage": "https://github.com/open-telemetry/opentelemetry-php/graphs/contributors" + } + ], + "require": { + "php": "^8.1", + "open-telemetry/sdk": "^1.0", + "tbachert/otel-sdk-configuration": "^0.1", + "tbachert/spi": "^0.2" + }, + "autoload": { + "psr-4": { + "OpenTelemetry\\Config\\SDK\\": "" + } + }, + "extra": { + "branch-alias": { + "dev-main": "0.1.x-dev" + }, + "spi": { + "Nevay\\OTelSDK\\Configuration\\ComponentProvider": [ + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Propagator\\TextMapPropagatorB3", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Propagator\\TextMapPropagatorB3Multi", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Propagator\\TextMapPropagatorBaggage", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Propagator\\TextMapPropagatorComposite", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Propagator\\TextMapPropagatorJaeger", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Propagator\\TextMapPropagatorTraceContext", + + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SamplerAlwaysOff", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SamplerAlwaysOn", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SamplerParentBased", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SamplerTraceIdRatioBased", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SpanExporterConsole", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SpanExporterOtlp", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SpanExporterZipkin", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SpanProcessorBatch", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SpanProcessorSimple", + + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Metrics\\AggregationResolverDefault", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Metrics\\MetricExporterConsole", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Metrics\\MetricExporterOtlp", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Metrics\\MetricReaderPeriodic", + + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordExporterConsole", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordExporterOtlp", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordProcessorBatch", + "OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordProcessorSimple" + ] + } + } +} diff --git a/src/Contrib/Otlp/OtlpUtil.php b/src/Contrib/Otlp/OtlpUtil.php index 62feb6d91..3de3085d6 100644 --- a/src/Contrib/Otlp/OtlpUtil.php +++ b/src/Contrib/Otlp/OtlpUtil.php @@ -4,6 +4,7 @@ namespace OpenTelemetry\Contrib\Otlp; +use function explode; use OpenTelemetry\API\Signals; use OpenTelemetry\SDK\Common\Configuration\Configuration; use OpenTelemetry\SDK\Common\Configuration\Variables; @@ -37,6 +38,22 @@ public static function method(string $signal): string return self::METHODS[$signal]; } + /** + * @param 'trace'|'metrics'|'logs' $signal + * @param 'grpc'|'http/protobuf'|'http/json' $protocol + */ + public static function path(string $signal, string $protocol): string + { + return match (explode('/', $protocol)[0]) { // @phpstan-ignore-line + 'grpc' => self::method($signal), + 'http' => match ($signal) { + Signals::TRACE => '/v1/traces', + Signals::METRICS => '/v1/metrics', + Signals::LOGS => '/v1/logs', + }, + }; + } + public static function getHeaders(string $signal): array { $headers = Configuration::has(self::HEADER_VARS[$signal]) ? diff --git a/src/Contrib/Otlp/Protocols.php b/src/Contrib/Otlp/Protocols.php index 96b04d8bf..736436a8f 100644 --- a/src/Contrib/Otlp/Protocols.php +++ b/src/Contrib/Otlp/Protocols.php @@ -27,6 +27,9 @@ public static function validate(string $protocol): void } } + /** + * @psalm-return ContentTypes::* + */ public static function contentType(string $protocol): string { self::validate($protocol); diff --git a/tests/Integration/Config/ConfigurationTest.php b/tests/Integration/Config/ConfigurationTest.php new file mode 100644 index 000000000..c5391ed24 --- /dev/null +++ b/tests/Integration/Config/ConfigurationTest.php @@ -0,0 +1,28 @@ +expectNotToPerformAssertions(); + Configuration::parseFile($file)->create(); + } + + public static function openTelemetryConfigurationDataProvider(): iterable + { + yield 'kitchen-sink' => [__DIR__ . '/configurations/kitchen-sink.yaml']; + yield 'anchors' => [__DIR__ . '/configurations/anchors.yaml']; + } +} diff --git a/tests/Integration/Config/configurations/anchors.yaml b/tests/Integration/Config/configurations/anchors.yaml new file mode 100644 index 000000000..18e409d1a --- /dev/null +++ b/tests/Integration/Config/configurations/anchors.yaml @@ -0,0 +1,41 @@ +# anchors.yaml demonstrates anchor substitution to reuse OTLP exporter configuration across signals. + +file_format: "0.1" +exporters: + otlp: &otlp-exporter + protocol: http/protobuf + endpoint: http://localhost:4318 + certificate: /app/cert.pem + client_key: /app/cert.pem + client_certificate: /app/cert.pem + headers: + api-key: !!str 1234 + compression: gzip + timeout: 10000 + +logger_provider: + processors: + - batch: + exporter: + otlp: + # expand the otlp-exporter anchor + <<: *otlp-exporter + +meter_provider: + readers: + - periodic: + interval: 5000 + timeout: 30000 + exporter: + otlp: + # expand the otlp-exporter anchor and add metric specific configuration + <<: *otlp-exporter + temporality_preference: delta + +tracer_provider: + processors: + - batch: + exporter: + otlp: + # expand the otlp-exporter anchor + <<: *otlp-exporter diff --git a/tests/Integration/Config/configurations/kitchen-sink.yaml b/tests/Integration/Config/configurations/kitchen-sink.yaml new file mode 100644 index 000000000..c331f6891 --- /dev/null +++ b/tests/Integration/Config/configurations/kitchen-sink.yaml @@ -0,0 +1,352 @@ +# kitchen-sink.yaml demonstrates all configurable surface area, including explanatory comments. +# +# It DOES NOT represent expected real world configuration, as it makes strange configuration +# choices in an effort to exercise the full surface area. +# +# Configuration values are set to their defaults when default values are defined. + +# The file format version +file_format: "0.1" + +# Configure if the SDK is disabled or not. This is not required to be provided +# to ensure the SDK isn't disabled, the default value when this is not provided +# is for the SDK to be enabled. +# +# Environment variable: OTEL_SDK_DISABLED +disabled: false + +# Configure general attribute limits. See also tracer_provider.limits, logger_provider.limits. +attribute_limits: + # Configure max attribute value size. + # + # Environment variable: OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT + attribute_value_length_limit: 4096 + # Configure max attribute count. + # + # Environment variable: OTEL_ATTRIBUTE_COUNT_LIMIT + attribute_count_limit: 128 + +# Configure logger provider. +logger_provider: + # Configure log record processors. + processors: + # Configure a batch log record processor. + - batch: + # Configure delay interval (in milliseconds) between two consecutive exports. + # + # Environment variable: OTEL_BLRP_SCHEDULE_DELAY + schedule_delay: 5000 + # Configure maximum allowed time (in milliseconds) to export data. + # + # Environment variable: OTEL_BLRP_EXPORT_TIMEOUT + export_timeout: 30000 + # Configure maximum queue size. + # + # Environment variable: OTEL_BLRP_MAX_QUEUE_SIZE + max_queue_size: 2048 + # Configure maximum batch size. + # + # Environment variable: OTEL_BLRP_MAX_EXPORT_BATCH_SIZE + max_export_batch_size: 512 + # Configure exporter. + # + # Environment variable: OTEL_LOGS_EXPORTER + exporter: + # Configure exporter to be OTLP. + otlp: + # Configure protocol. + # + # Environment variable: OTEL_EXPORTER_OTLP_PROTOCOL, OTEL_EXPORTER_OTLP_LOGS_PROTOCOL + protocol: http/protobuf + # Configure endpoint. + # + # Environment variable: OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_LOGS_ENDPOINT + endpoint: http://localhost:4318 + # Configure certificate. + # + # Environment variable: OTEL_EXPORTER_OTLP_CERTIFICATE, OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE + certificate: /app/cert.pem + # Configure mTLS private client key. + # + # Environment variable: OTEL_EXPORTER_OTLP_CLIENT_KEY, OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY + client_key: /app/cert.pem + # Configure mTLS client certificate. + # + # Environment variable: OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE, OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE + client_certificate: /app/cert.pem + # Configure headers. + # + # Environment variable: OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_LOGS_HEADERS + headers: + api-key: "1234" + # Configure compression. + # + # Environment variable: OTEL_EXPORTER_OTLP_COMPRESSION, OTEL_EXPORTER_OTLP_LOGS_COMPRESSION + compression: gzip + # Configure max time (in milliseconds) to wait for each export. + # + # Environment variable: OTEL_EXPORTER_OTLP_TIMEOUT, OTEL_EXPORTER_OTLP_LOGS_TIMEOUT + timeout: 10000 + # Configure log record limits. See also attribute_limits. + limits: + # Configure max log record attribute value size. Overrides attribute_limits.attribute_value_length_limit. + # + # Environment variable: OTEL_LOGRECORD_ATTRIBUTE_VALUE_LENGTH_LIMIT + attribute_value_length_limit: 4096 + # Configure max log record attribute count. Overrides attribute_limits.attribute_count_limit. + # + # Environment variable: OTEL_LOGRECORD_ATTRIBUTE_COUNT_LIMIT + attribute_count_limit: 128 + +# Configure meter provider. +meter_provider: + # Configure metric readers. + readers: + # Configure a periodic metric reader. + - periodic: + # Configure delay interval (in milliseconds) between start of two consecutive exports. + # + # Environment variable: OTEL_METRIC_EXPORT_INTERVAL + interval: 5000 + # Configure maximum allowed time (in milliseconds) to export data. + # + # Environment variable: OTEL_METRIC_EXPORT_TIMEOUT + timeout: 30000 + # Configure exporter. + # + # Environment variable: OTEL_METRICS_EXPORTER + exporter: + # Configure exporter to be OTLP. + otlp: + # Configure protocol. + # + # Environment variable: OTEL_EXPORTER_OTLP_PROTOCOL, OTEL_EXPORTER_OTLP_METRICS_PROTOCOL + protocol: http/protobuf + # Configure endpoint. + # + # Environment variable: OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_METRICS_ENDPOINT + endpoint: http://localhost:4318 + # Configure certificate. + # + # Environment variable: OTEL_EXPORTER_OTLP_CERTIFICATE, OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE + certificate: /app/cert.pem + # Configure mTLS private client key. + # + # Environment variable: OTEL_EXPORTER_OTLP_CLIENT_KEY, OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY + client_key: /app/cert.pem + # Configure mTLS client certificate. + # + # Environment variable: OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE, OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE + client_certificate: /app/cert.pem + # Configure headers. + # + # Environment variable: OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_METRICS_HEADERS + headers: + api-key: !!str 1234 + # Configure compression. + # + # Environment variable: OTEL_EXPORTER_OTLP_COMPRESSION, OTEL_EXPORTER_OTLP_METRICS_COMPRESSION + compression: gzip + # Configure max time (in milliseconds) to wait for each export. + # + # Environment variable: OTEL_EXPORTER_OTLP_TIMEOUT, OTEL_EXPORTER_OTLP_METRICS_TIMEOUT + timeout: 10000 + # Configure temporality preference. + # + # Environment variable: OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE + temporality_preference: delta + # Configure a periodic metric reader. + - periodic: + # Configure exporter. + exporter: + # Configure exporter to be console. + console: {} + # Configure views. Each view has a selector which determines the instrument(s) it applies to, and a configuration for the resulting stream(s). + views: + # Configure a view. + - selector: + # Configure instrument name selection criteria. + instrument_name: my-instrument + # Configure instrument type selection criteria. + instrument_type: histogram + # Configure the instrument unit selection criteria. + unit: ms + # Configure meter name selection criteria. + meter_name: my-meter + # Configure meter version selection criteria. + meter_version: 1.0.0 + # Configure meter schema url selection criteria. + meter_schema_url: https://opentelemetry.io/schemas/1.16.0 + # Configure stream. + stream: + # Configure metric name of the resulting stream(s). + name: new_instrument_name + # Configure metric description of the resulting stream(s). + description: new_description + # Configure aggregation of the resulting stream(s). Known values include: default, drop, explicit_bucket_histogram, base2_exponential_bucket_histogram, last_value, sum. + aggregation: ~ + # Configure attribute keys retained in the resulting stream(s). + attribute_keys: + - key1 + - key2 + +# Configure text map context propagators. +# +# Environment variable: OTEL_PROPAGATORS +propagator: + composite: [tracecontext, baggage, b3, b3multi, jaeger] + +# Configure tracer provider. +tracer_provider: + # Configure span processors. + processors: + # Configure a batch span processor. + - batch: + # Configure delay interval (in milliseconds) between two consecutive exports. + # + # Environment variable: OTEL_BSP_SCHEDULE_DELAY + schedule_delay: 5000 + # Configure maximum allowed time (in milliseconds) to export data. + # + # Environment variable: OTEL_BSP_EXPORT_TIMEOUT + export_timeout: 30000 + # Configure maximum queue size. + # + # Environment variable: OTEL_BSP_MAX_QUEUE_SIZE + max_queue_size: 2048 + # Configure maximum batch size. + # + # Environment variable: OTEL_BSP_MAX_EXPORT_BATCH_SIZE + max_export_batch_size: 512 + # Configure exporter. + # + # Environment variable: OTEL_TRACES_EXPORTER + exporter: + # Configure exporter to be OTLP. + otlp: + # Configure protocol. + # + # Environment variable: OTEL_EXPORTER_OTLP_PROTOCOL, OTEL_EXPORTER_OTLP_TRACES_PROTOCOL + protocol: http/protobuf + # Configure endpoint. + # + # Environment variable: OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_TRACES_ENDPOINT + endpoint: http://localhost:4318 + # Configure certificate. + # + # Environment variable: OTEL_EXPORTER_OTLP_CERTIFICATE, OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE + certificate: /app/cert.pem + # Configure mTLS private client key. + # + # Environment variable: OTEL_EXPORTER_OTLP_CLIENT_KEY, OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY + client_key: /app/cert.pem + # Configure mTLS client certificate. + # + # Environment variable: OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE, OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE + client_certificate: /app/cert.pem + # Configure headers. + # + # Environment variable: OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_TRACES_HEADERS + headers: + api-key: !!str 1234 + # Configure compression. + # + # Environment variable: OTEL_EXPORTER_OTLP_COMPRESSION, OTEL_EXPORTER_OTLP_TRACES_COMPRESSION + compression: gzip + # Configure max time (in milliseconds) to wait for each export. + # + # Environment variable: OTEL_EXPORTER_OTLP_TIMEOUT, OTEL_EXPORTER_OTLP_TRACES_TIMEOUT + timeout: 10000 + # Configure a batch span processor. + - batch: + # Configure exporter. + # + # Environment variable: OTEL_TRACES_EXPORTER + exporter: + # Configure exporter to be zipkin. + zipkin: + # Configure endpoint. + # + # Environment variable: OTEL_EXPORTER_ZIPKIN_ENDPOINT + endpoint: http://localhost:9411/api/v2/spans + # Configure max time (in milliseconds) to wait for each export. + # + # Environment variable: OTEL_EXPORTER_ZIPKIN_TIMEOUT + timeout: 10000 + # Configure a simple span processor. + - simple: + # Configure exporter. + exporter: + # Configure exporter to be console. + console: {} + # Configure span limits. See also attribute_limits. + limits: + # Configure max span attribute value size. Overrides attribute_limits.attribute_value_length_limit. + # + # Environment variable: OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT + attribute_value_length_limit: 4096 + # Configure max span attribute count. Overrides attribute_limits.attribute_count_limit. + # + # Environment variable: OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT + attribute_count_limit: 128 + # Configure max span event count. + # + # Environment variable: OTEL_SPAN_EVENT_COUNT_LIMIT + event_count_limit: 128 + # Configure max span link count. + # + # Environment variable: OTEL_SPAN_LINK_COUNT_LIMIT + link_count_limit: 128 + # Configure max attributes per span event. + # + # Environment variable: OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT + event_attribute_count_limit: 128 + # Configure max attributes per span link. + # + # Environment variable: OTEL_LINK_ATTRIBUTE_COUNT_LIMIT + link_attribute_count_limit: 128 + # Configure the sampler. + sampler: + # Configure sampler to be parent_based. Known values include: always_off, always_on, jaeger_remote, parent_based, trace_id_ratio_based. + # + # Environment variable: OTEL_TRACES_SAMPLER=parentbased_* + parent_based: + # Configure root sampler. + # + # Environment variable: OTEL_TRACES_SAMPLER=parentbased_traceidratio + root: + # Configure sampler to be trace_id_ratio_based. + trace_id_ratio_based: + # Configure trace_id_ratio. + # + # Environment variable: OTEL_TRACES_SAMPLER_ARG=traceidratio=0.0001 + ratio: 0.0001 + # Configure remote_parent_sampled sampler. + remote_parent_sampled: + # Configure sampler to be always_on. + always_on: {} + # Configure remote_parent_not_sampled sampler. + remote_parent_not_sampled: + # Configure sampler to be always_off. + always_off: {} + # Configure local_parent_sampled sampler. + local_parent_sampled: + # Configure sampler to be always_on. + always_on: {} + # Configure local_parent_not_sampled sampler. + local_parent_not_sampled: + # Configure sampler to be always_off. + always_off: {} + +# Configure resource for all signals. +resource: + # Configure resource attributes. + # + # Environment variable: OTEL_RESOURCE_ATTRIBUTES + attributes: + # Configure `service.name` resource attribute + # + # Environment variable: OTEL_SERVICE_NAME + service.name: !!str "unknown_service" + # Configure the resource schema URL. + schema_url: https://opentelemetry.io/schemas/1.23.1