From f2d964bcfae00cfda35827528c219ac333654ec8 Mon Sep 17 00:00:00 2001 From: Mike Goldsmith Date: Mon, 20 Apr 2026 11:53:30 +0100 Subject: [PATCH 1/2] add exporter plugin loading to declarative config for all three signals SpanExporter, LogRecordExporter, and PushMetricExporter are changed from @dataclass to TypeAlias = dict[str, Any] in models.py, preserving unknown exporter names as dict keys through the config pipeline. Each signal's factory function (_create_span_exporter, _create_log_record_exporter, _create_push_metric_exporter) now has a registry of known exporter names (otlp_http, otlp_grpc, console) with fallback to load_entry_point for unknown names via the opentelemetry_{traces,logs,metrics}_exporter entry point groups. _parse_headers in _common.py is updated to handle both NameStringValuePair objects (typed config) and raw dicts (YAML path), making the full dict path testable end-to-end. Assisted-by: Claude Opus 4.6 --- CHANGELOG.md | 2 + .../sdk/_configuration/_common.py | 5 ++- .../sdk/_configuration/_logger_provider.py | 44 ++++++++++--------- .../sdk/_configuration/_meter_provider.py | 44 ++++++++++--------- .../sdk/_configuration/_tracer_provider.py | 42 +++++++++++------- .../sdk/_configuration/models.py | 30 +++++-------- .../_configuration/test_logger_provider.py | 23 +++++++--- .../_configuration/test_meter_provider.py | 20 +++++++++ .../_configuration/test_tracer_provider.py | 22 ++++++++++ 9 files changed, 152 insertions(+), 80 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6024431107..a84571cd4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +- `opentelemetry-sdk`: add exporter plugin loading to declarative file configuration for all three signals (traces, metrics, logs) via the `opentelemetry_*_exporter` entry point groups + ([#5069](https://github.com/open-telemetry/opentelemetry-python/pull/5069)) - `opentelemetry-sdk`: add `load_entry_point` shared utility to declarative file configuration for loading plugins via entry points; refactor propagator loading to use it ([#5093](https://github.com/open-telemetry/opentelemetry-python/pull/5093)) - `opentelemetry-sdk`: fix YAML structure injection via environment variable substitution in declarative file configuration; values containing newlines are now emitted as quoted YAML scalars per spec requirement diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_common.py b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_common.py index 0498a19e13..92c1665419 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_common.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_common.py @@ -73,5 +73,8 @@ def _parse_headers( ) if headers: for pair in headers: - result[pair.name] = pair.value or "" + if isinstance(pair, dict): + result[pair["name"]] = pair.get("value") or "" + else: + result[pair.name] = pair.value or "" return result diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_logger_provider.py b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_logger_provider.py index 61673238b7..7156507e3e 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_logger_provider.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_logger_provider.py @@ -18,7 +18,10 @@ from typing import Optional from opentelemetry._logs import set_logger_provider -from opentelemetry.sdk._configuration._common import _parse_headers +from opentelemetry.sdk._configuration._common import ( + _parse_headers, + load_entry_point, +) from opentelemetry.sdk._configuration._exceptions import ConfigurationError from opentelemetry.sdk._configuration.models import ( BatchLogRecordProcessor as BatchLogRecordProcessorConfig, @@ -26,9 +29,6 @@ from opentelemetry.sdk._configuration.models import ( LoggerProvider as LoggerProviderConfig, ) -from opentelemetry.sdk._configuration.models import ( - LogRecordExporter as LogRecordExporterConfig, -) from opentelemetry.sdk._configuration.models import ( LogRecordProcessor as LogRecordProcessorConfig, ) @@ -136,24 +136,28 @@ def _create_otlp_grpc_log_exporter( ) -def _create_log_record_exporter( - config: LogRecordExporterConfig, -) -> LogRecordExporter: - """Create a log record exporter from config.""" - if config.console is not None: - return _create_console_log_exporter() - if config.otlp_http is not None: - return _create_otlp_http_log_exporter(config.otlp_http) - if config.otlp_grpc is not None: - return _create_otlp_grpc_log_exporter(config.otlp_grpc) - if config.otlp_file_development is not None: +_LOG_EXPORTER_REGISTRY: dict = { + "otlp_http": _create_otlp_http_log_exporter, + "otlp_grpc": _create_otlp_grpc_log_exporter, + "console": lambda _: _create_console_log_exporter(), +} + + +def _create_log_record_exporter(config: dict) -> LogRecordExporter: + """Create a log record exporter from a config dict with a single key naming the exporter type. + + Known names (otlp_http, otlp_grpc, console) are bootstrapped directly. + Unknown names are loaded via the ``opentelemetry_logs_exporter`` entry + point group, matching the spec's PluginComponentProvider mechanism. + """ + if len(config) != 1: raise ConfigurationError( - "otlp_file_development log exporter is experimental and not yet supported." + f"Log exporter config must have exactly one key, got: {list(config.keys())}" ) - raise ConfigurationError( - "No exporter type specified in log record exporter config. " - "Supported types: console, otlp_http, otlp_grpc." - ) + name, exporter_config = next(iter(config.items())) + if name in _LOG_EXPORTER_REGISTRY: + return _LOG_EXPORTER_REGISTRY[name](exporter_config) + return load_entry_point("opentelemetry_logs_exporter", name)() def _create_batch_log_record_processor( diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_meter_provider.py b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_meter_provider.py index 257351135f..fa35fd9977 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_meter_provider.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_meter_provider.py @@ -18,7 +18,10 @@ from typing import Optional, Set, Type from opentelemetry import metrics -from opentelemetry.sdk._configuration._common import _parse_headers +from opentelemetry.sdk._configuration._common import ( + _parse_headers, + load_entry_point, +) from opentelemetry.sdk._configuration._exceptions import ConfigurationError from opentelemetry.sdk._configuration.models import ( Aggregation as AggregationConfig, @@ -49,9 +52,6 @@ from opentelemetry.sdk._configuration.models import ( PeriodicMetricReader as PeriodicMetricReaderConfig, ) -from opentelemetry.sdk._configuration.models import ( - PushMetricExporter as PushMetricExporterConfig, -) from opentelemetry.sdk._configuration.models import ( View as ViewConfig, ) @@ -349,24 +349,28 @@ def _create_otlp_grpc_metric_exporter( ) -def _create_push_metric_exporter( - config: PushMetricExporterConfig, -) -> MetricExporter: - """Create a push metric exporter from config.""" - if config.console is not None: - return _create_console_metric_exporter(config.console) - if config.otlp_http is not None: - return _create_otlp_http_metric_exporter(config.otlp_http) - if config.otlp_grpc is not None: - return _create_otlp_grpc_metric_exporter(config.otlp_grpc) - if config.otlp_file_development is not None: +_METRIC_EXPORTER_REGISTRY: dict = { + "otlp_http": _create_otlp_http_metric_exporter, + "otlp_grpc": _create_otlp_grpc_metric_exporter, + "console": _create_console_metric_exporter, +} + + +def _create_push_metric_exporter(config: dict) -> MetricExporter: + """Create a push metric exporter from a config dict with a single key naming the exporter type. + + Known names (otlp_http, otlp_grpc, console) are bootstrapped directly. + Unknown names are loaded via the ``opentelemetry_metrics_exporter`` entry + point group, matching the spec's PluginComponentProvider mechanism. + """ + if len(config) != 1: raise ConfigurationError( - "otlp_file_development metric exporter is experimental and not yet supported." + f"Metric exporter config must have exactly one key, got: {list(config.keys())}" ) - raise ConfigurationError( - "No exporter type specified in push metric exporter config. " - "Supported types: console, otlp_http, otlp_grpc." - ) + name, exporter_config = next(iter(config.items())) + if name in _METRIC_EXPORTER_REGISTRY: + return _METRIC_EXPORTER_REGISTRY[name](exporter_config) + return load_entry_point("opentelemetry_metrics_exporter", name)() def _create_periodic_metric_reader( diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_tracer_provider.py b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_tracer_provider.py index 32dfd96567..687a0ec10c 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_tracer_provider.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_tracer_provider.py @@ -18,7 +18,10 @@ from typing import Optional from opentelemetry import trace -from opentelemetry.sdk._configuration._common import _parse_headers +from opentelemetry.sdk._configuration._common import ( + _parse_headers, + load_entry_point, +) from opentelemetry.sdk._configuration._exceptions import ConfigurationError from opentelemetry.sdk._configuration.models import ( OtlpGrpcExporter as OtlpGrpcExporterConfig, @@ -32,9 +35,6 @@ from opentelemetry.sdk._configuration.models import ( Sampler as SamplerConfig, ) -from opentelemetry.sdk._configuration.models import ( - SpanExporter as SpanExporterConfig, -) from opentelemetry.sdk._configuration.models import ( SpanLimits as SpanLimitsConfig, ) @@ -146,18 +146,28 @@ def _create_otlp_grpc_span_exporter( ) -def _create_span_exporter(config: SpanExporterConfig) -> SpanExporter: - """Create a span exporter from config.""" - if config.otlp_http is not None: - return _create_otlp_http_span_exporter(config.otlp_http) - if config.otlp_grpc is not None: - return _create_otlp_grpc_span_exporter(config.otlp_grpc) - if config.console is not None: - return ConsoleSpanExporter() - raise ConfigurationError( - "No exporter type specified in span exporter config. " - "Supported types: otlp_http, otlp_grpc, console." - ) +_SPAN_EXPORTER_REGISTRY: dict = { + "otlp_http": _create_otlp_http_span_exporter, + "otlp_grpc": _create_otlp_grpc_span_exporter, + "console": lambda _: ConsoleSpanExporter(), +} + + +def _create_span_exporter(config: dict) -> SpanExporter: + """Create a span exporter from a config dict with a single key naming the exporter type. + + Known names (otlp_http, otlp_grpc, console) are bootstrapped directly. + Unknown names are loaded via the ``opentelemetry_traces_exporter`` entry + point group, matching the spec's PluginComponentProvider mechanism. + """ + if len(config) != 1: + raise ConfigurationError( + f"Span exporter config must have exactly one key, got: {list(config.keys())}" + ) + name, exporter_config = next(iter(config.items())) + if name in _SPAN_EXPORTER_REGISTRY: + return _SPAN_EXPORTER_REGISTRY[name](exporter_config) + return load_entry_point("opentelemetry_traces_exporter", name)() def _create_span_processor( diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/models.py b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/models.py index 5159137228..abd80a5698 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/models.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/models.py @@ -350,12 +350,10 @@ class SeverityNumber(Enum): fatal4 = "fatal4" -@dataclass -class SpanExporter: - otlp_http: OtlpHttpExporter | None = None - otlp_grpc: OtlpGrpcExporter | None = None - otlp_file_development: ExperimentalOtlpFileExporter | None = None - console: ConsoleExporter | None = None +# Diverges from codegen: SpanExporter is typed as dict[str, Any] rather than +# a dataclass so that unknown exporter names (plugin/custom exporters) are +# preserved as dict keys through the config pipeline. +SpanExporter: TypeAlias = dict[str, Any] class SpanKind(Enum): @@ -524,12 +522,10 @@ class ExperimentalTracerConfigurator: tracers: list[ExperimentalTracerMatcherAndConfig] | None = None -@dataclass -class LogRecordExporter: - otlp_http: OtlpHttpExporter | None = None - otlp_grpc: OtlpGrpcExporter | None = None - otlp_file_development: ExperimentalOtlpFileExporter | None = None - console: ConsoleExporter | None = None +# Diverges from codegen: LogRecordExporter is typed as dict[str, Any] rather +# than a dataclass so that unknown exporter names (plugin/custom exporters) +# are preserved as dict keys through the config pipeline. +LogRecordExporter: TypeAlias = dict[str, Any] @dataclass @@ -549,12 +545,10 @@ class PullMetricReader: cardinality_limits: CardinalityLimits | None = None -@dataclass -class PushMetricExporter: - otlp_http: OtlpHttpMetricExporter | None = None - otlp_grpc: OtlpGrpcMetricExporter | None = None - otlp_file_development: ExperimentalOtlpFileMetricExporter | None = None - console: ConsoleMetricExporter | None = None +# Diverges from codegen: PushMetricExporter is typed as dict[str, Any] rather +# than a dataclass so that unknown exporter names (plugin/custom exporters) +# are preserved as dict keys through the config pipeline. +PushMetricExporter: TypeAlias = dict[str, Any] @dataclass diff --git a/opentelemetry-sdk/tests/_configuration/test_logger_provider.py b/opentelemetry-sdk/tests/_configuration/test_logger_provider.py index ff820d105a..0116a4a2e1 100644 --- a/opentelemetry-sdk/tests/_configuration/test_logger_provider.py +++ b/opentelemetry-sdk/tests/_configuration/test_logger_provider.py @@ -228,16 +228,29 @@ def test_console_exporter(self): exporter = _create_log_record_exporter(config) self.assertIsInstance(exporter, ConsoleLogRecordExporter) - def test_otlp_file_development_raises(self): - config = LogRecordExporterConfig(otlp_file_development={}) - with self.assertRaises(ConfigurationError): - _create_log_record_exporter(config) - def test_no_exporter_type_raises(self): config = LogRecordExporterConfig() with self.assertRaises(ConfigurationError): _create_log_record_exporter(config) + def test_plugin_log_exporter_loaded_via_entry_point(self): + mock_exporter = MagicMock() + mock_class = MagicMock(return_value=mock_exporter) + with patch( + "opentelemetry.sdk._configuration._common.entry_points", + return_value=[MagicMock(**{"load.return_value": mock_class})], + ): + result = _create_log_record_exporter({"my_custom_exporter": {}}) + self.assertIs(result, mock_exporter) + + def test_unknown_log_exporter_raises_configuration_error(self): + with patch( + "opentelemetry.sdk._configuration._common.entry_points", + return_value=[], + ): + with self.assertRaises(ConfigurationError): + _create_log_record_exporter({"no_such_exporter": {}}) + def test_otlp_http_missing_package_raises(self): config = LogRecordExporterConfig( otlp_http=OtlpHttpExporterConfig(endpoint="http://localhost:4318") diff --git a/opentelemetry-sdk/tests/_configuration/test_meter_provider.py b/opentelemetry-sdk/tests/_configuration/test_meter_provider.py index 04d60847f0..c101144cae 100644 --- a/opentelemetry-sdk/tests/_configuration/test_meter_provider.py +++ b/opentelemetry-sdk/tests/_configuration/test_meter_provider.py @@ -306,6 +306,26 @@ def test_no_exporter_type_raises(self): with self.assertRaises(ConfigurationError): create_meter_provider(config) + def test_plugin_metric_exporter_loaded_via_entry_point(self): + mock_exporter = MagicMock() + mock_class = MagicMock(return_value=mock_exporter) + with patch( + "opentelemetry.sdk._configuration._common.entry_points", + return_value=[MagicMock(**{"load.return_value": mock_class})], + ): + config = self._make_periodic_config({"my_custom_exporter": {}}) + provider = create_meter_provider(config) + self.assertEqual(len(provider._sdk_config.metric_readers), 1) + + def test_unknown_metric_exporter_raises_configuration_error(self): + with patch( + "opentelemetry.sdk._configuration._common.entry_points", + return_value=[], + ): + config = self._make_periodic_config({"no_such_exporter": {}}) + with self.assertRaises(ConfigurationError): + create_meter_provider(config) + def test_multiple_readers(self): config = MeterProviderConfig( readers=[ diff --git a/opentelemetry-sdk/tests/_configuration/test_tracer_provider.py b/opentelemetry-sdk/tests/_configuration/test_tracer_provider.py index 5caf077cd5..4d0f4e9c8d 100644 --- a/opentelemetry-sdk/tests/_configuration/test_tracer_provider.py +++ b/opentelemetry-sdk/tests/_configuration/test_tracer_provider.py @@ -361,6 +361,28 @@ def test_no_exporter_type_raises(self): with self.assertRaises(ConfigurationError): create_tracer_provider(config) + def test_plugin_span_exporter_loaded_via_entry_point(self): + mock_exporter = MagicMock() + mock_class = MagicMock(return_value=mock_exporter) + with patch( + "opentelemetry.sdk._configuration._common.entry_points", + return_value=[MagicMock(**{"load.return_value": mock_class})], + ): + config = self._make_batch_config({"my_custom_exporter": {}}) + provider = create_tracer_provider(config) + self.assertEqual( + len(provider._active_span_processor._span_processors), 1 + ) + + def test_unknown_span_exporter_raises_configuration_error(self): + with patch( + "opentelemetry.sdk._configuration._common.entry_points", + return_value=[], + ): + config = self._make_batch_config({"no_such_exporter": {}}) + with self.assertRaises(ConfigurationError): + create_tracer_provider(config) + class TestCreateSpanLimits(unittest.TestCase): # pylint: disable=no-self-use From 25e92570ddc505d2df73206b7da20785728be6b2 Mon Sep 17 00:00:00 2001 From: Mike Goldsmith Date: Mon, 20 Apr 2026 14:31:09 +0100 Subject: [PATCH 2/2] revert models.py changes, use raw dicts without modifying codegen The generated models are unchanged. Python dataclasses don't enforce field types at runtime, so parent fields like BatchSpanProcessor.exporter naturally accept raw dicts (from the YAML loader) alongside typed instances. Tests updated to pass raw dicts consistently. Assisted-by: Claude Opus 4.6 --- .../sdk/_configuration/models.py | 30 ++++++----- .../_configuration/test_logger_provider.py | 51 +++++++++---------- .../_configuration/test_meter_provider.py | 43 +++++++--------- .../_configuration/test_tracer_provider.py | 25 ++++----- 4 files changed, 70 insertions(+), 79 deletions(-) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/models.py b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/models.py index abd80a5698..5159137228 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/models.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/models.py @@ -350,10 +350,12 @@ class SeverityNumber(Enum): fatal4 = "fatal4" -# Diverges from codegen: SpanExporter is typed as dict[str, Any] rather than -# a dataclass so that unknown exporter names (plugin/custom exporters) are -# preserved as dict keys through the config pipeline. -SpanExporter: TypeAlias = dict[str, Any] +@dataclass +class SpanExporter: + otlp_http: OtlpHttpExporter | None = None + otlp_grpc: OtlpGrpcExporter | None = None + otlp_file_development: ExperimentalOtlpFileExporter | None = None + console: ConsoleExporter | None = None class SpanKind(Enum): @@ -522,10 +524,12 @@ class ExperimentalTracerConfigurator: tracers: list[ExperimentalTracerMatcherAndConfig] | None = None -# Diverges from codegen: LogRecordExporter is typed as dict[str, Any] rather -# than a dataclass so that unknown exporter names (plugin/custom exporters) -# are preserved as dict keys through the config pipeline. -LogRecordExporter: TypeAlias = dict[str, Any] +@dataclass +class LogRecordExporter: + otlp_http: OtlpHttpExporter | None = None + otlp_grpc: OtlpGrpcExporter | None = None + otlp_file_development: ExperimentalOtlpFileExporter | None = None + console: ConsoleExporter | None = None @dataclass @@ -545,10 +549,12 @@ class PullMetricReader: cardinality_limits: CardinalityLimits | None = None -# Diverges from codegen: PushMetricExporter is typed as dict[str, Any] rather -# than a dataclass so that unknown exporter names (plugin/custom exporters) -# are preserved as dict keys through the config pipeline. -PushMetricExporter: TypeAlias = dict[str, Any] +@dataclass +class PushMetricExporter: + otlp_http: OtlpHttpMetricExporter | None = None + otlp_grpc: OtlpGrpcMetricExporter | None = None + otlp_file_development: ExperimentalOtlpFileMetricExporter | None = None + console: ConsoleMetricExporter | None = None @dataclass diff --git a/opentelemetry-sdk/tests/_configuration/test_logger_provider.py b/opentelemetry-sdk/tests/_configuration/test_logger_provider.py index 0116a4a2e1..3de8b5cbf9 100644 --- a/opentelemetry-sdk/tests/_configuration/test_logger_provider.py +++ b/opentelemetry-sdk/tests/_configuration/test_logger_provider.py @@ -39,9 +39,6 @@ from opentelemetry.sdk._configuration.models import ( LoggerProvider as LoggerProviderConfig, ) -from opentelemetry.sdk._configuration.models import ( - LogRecordExporter as LogRecordExporterConfig, -) from opentelemetry.sdk._configuration.models import ( LogRecordLimits as LogRecordLimitsConfig, ) @@ -100,7 +97,7 @@ def _make_batch_config( max_export_batch_size=None, ): if exporter_config is None: - exporter_config = LogRecordExporterConfig(console={}) + exporter_config = {"console": {}} return BatchLogRecordProcessorConfig( exporter=exporter_config, schedule_delay=schedule_delay, @@ -184,9 +181,7 @@ def test_batch_processor_uses_console_exporter(self): ) def test_simple_processor_uses_console_exporter(self): - config = SimpleLogRecordProcessorConfig( - exporter=LogRecordExporterConfig(console={}) - ) + config = SimpleLogRecordProcessorConfig(exporter={"console": {}}) processor = _create_simple_log_record_processor(config) self.assertIsInstance(processor, SimpleLogRecordProcessor) self.assertIsInstance(processor._exporter, ConsoleLogRecordExporter) @@ -198,9 +193,7 @@ def test_batch_processor_dispatched_from_processor_config(self): def test_simple_processor_dispatched_from_processor_config(self): config = LogRecordProcessorConfig( - simple=SimpleLogRecordProcessorConfig( - exporter=LogRecordExporterConfig(console={}) - ) + simple=SimpleLogRecordProcessorConfig(exporter={"console": {}}) ) processor = _create_log_record_processor(config) self.assertIsInstance(processor, SimpleLogRecordProcessor) @@ -224,12 +217,12 @@ def test_batch_processor_suppresses_env_var(self): class TestCreateLogRecordExporters(unittest.TestCase): def test_console_exporter(self): - config = LogRecordExporterConfig(console={}) + config = {"console": {}} exporter = _create_log_record_exporter(config) self.assertIsInstance(exporter, ConsoleLogRecordExporter) def test_no_exporter_type_raises(self): - config = LogRecordExporterConfig() + config = {} with self.assertRaises(ConfigurationError): _create_log_record_exporter(config) @@ -252,9 +245,11 @@ def test_unknown_log_exporter_raises_configuration_error(self): _create_log_record_exporter({"no_such_exporter": {}}) def test_otlp_http_missing_package_raises(self): - config = LogRecordExporterConfig( - otlp_http=OtlpHttpExporterConfig(endpoint="http://localhost:4318") - ) + config = { + "otlp_http": OtlpHttpExporterConfig( + endpoint="http://localhost:4318" + ) + } with patch.dict( sys.modules, { @@ -266,9 +261,11 @@ def test_otlp_http_missing_package_raises(self): _create_log_record_exporter(config) def test_otlp_grpc_missing_package_raises(self): - config = LogRecordExporterConfig( - otlp_grpc=OtlpGrpcExporterConfig(endpoint="http://localhost:4317") - ) + config = { + "otlp_grpc": OtlpGrpcExporterConfig( + endpoint="http://localhost:4317" + ) + } with patch.dict( sys.modules, { @@ -296,12 +293,12 @@ def test_otlp_http_exporter_endpoint(self): "opentelemetry.exporter.otlp.proto.http._log_exporter": mock_log_module, }, ): - config = LogRecordExporterConfig( - otlp_http=OtlpHttpExporterConfig( + config = { + "otlp_http": OtlpHttpExporterConfig( endpoint="http://collector:4318", timeout=5000, ) - ) + } _create_log_record_exporter(config) mock_exporter_cls.assert_called_once() @@ -324,13 +321,13 @@ def test_otlp_http_exporter_headers(self): "opentelemetry.exporter.otlp.proto.http._log_exporter": mock_log_module, }, ): - config = LogRecordExporterConfig( - otlp_http=OtlpHttpExporterConfig( + config = { + "otlp_http": OtlpHttpExporterConfig( headers=[ NameStringValuePair(name="x-api-key", value="secret") ] ) - ) + } _create_log_record_exporter(config) call_kwargs = mock_exporter_cls.call_args.kwargs @@ -350,12 +347,12 @@ def test_otlp_grpc_exporter_endpoint(self): "opentelemetry.exporter.otlp.proto.grpc._log_exporter": mock_grpc_log_module, }, ): - config = LogRecordExporterConfig( - otlp_grpc=OtlpGrpcExporterConfig( + config = { + "otlp_grpc": OtlpGrpcExporterConfig( endpoint="http://collector:4317", timeout=10000, ) - ) + } _create_log_record_exporter(config) mock_exporter_cls.assert_called_once() diff --git a/opentelemetry-sdk/tests/_configuration/test_meter_provider.py b/opentelemetry-sdk/tests/_configuration/test_meter_provider.py index c101144cae..94647d8075 100644 --- a/opentelemetry-sdk/tests/_configuration/test_meter_provider.py +++ b/opentelemetry-sdk/tests/_configuration/test_meter_provider.py @@ -63,9 +63,6 @@ from opentelemetry.sdk._configuration.models import ( PeriodicMetricReader as PeriodicMetricReaderConfig, ) -from opentelemetry.sdk._configuration.models import ( - PushMetricExporter as PushMetricExporterConfig, -) from opentelemetry.sdk._configuration.models import ( View as ViewConfig, ) @@ -132,9 +129,7 @@ def test_none_config_does_not_read_interval_env_var(self): readers=[ MetricReaderConfig( periodic=PeriodicMetricReaderConfig( - exporter=PushMetricExporterConfig( - console=ConsoleMetricExporterConfig() - ) + exporter={"console": ConsoleMetricExporterConfig()} ) ) ] @@ -188,7 +183,7 @@ def _make_periodic_config(exporter_config, interval=None, timeout=None): def test_console_exporter(self): config = self._make_periodic_config( - PushMetricExporterConfig(console=ConsoleMetricExporterConfig()) + {"console": ConsoleMetricExporterConfig()} ) provider = create_meter_provider(config) reader = provider._sdk_config.metric_readers[0] @@ -197,7 +192,7 @@ def test_console_exporter(self): def test_periodic_reader_default_interval(self): config = self._make_periodic_config( - PushMetricExporterConfig(console=ConsoleMetricExporterConfig()) + {"console": ConsoleMetricExporterConfig()} ) provider = create_meter_provider(config) reader = provider._sdk_config.metric_readers[0] @@ -205,7 +200,7 @@ def test_periodic_reader_default_interval(self): def test_periodic_reader_default_timeout(self): config = self._make_periodic_config( - PushMetricExporterConfig(console=ConsoleMetricExporterConfig()) + {"console": ConsoleMetricExporterConfig()} ) provider = create_meter_provider(config) reader = provider._sdk_config.metric_readers[0] @@ -213,7 +208,7 @@ def test_periodic_reader_default_timeout(self): def test_periodic_reader_explicit_interval(self): config = self._make_periodic_config( - PushMetricExporterConfig(console=ConsoleMetricExporterConfig()), + {"console": ConsoleMetricExporterConfig()}, interval=5000, ) provider = create_meter_provider(config) @@ -222,7 +217,7 @@ def test_periodic_reader_explicit_interval(self): def test_periodic_reader_explicit_timeout(self): config = self._make_periodic_config( - PushMetricExporterConfig(console=ConsoleMetricExporterConfig()), + {"console": ConsoleMetricExporterConfig()}, timeout=10000, ) provider = create_meter_provider(config) @@ -231,7 +226,7 @@ def test_periodic_reader_explicit_timeout(self): def test_otlp_http_missing_package_raises(self): config = self._make_periodic_config( - PushMetricExporterConfig(otlp_http=OtlpHttpMetricExporterConfig()) + {"otlp_http": OtlpHttpMetricExporterConfig()} ) with patch.dict( sys.modules, @@ -260,11 +255,11 @@ def test_otlp_http_created_with_endpoint(self): }, ): config = self._make_periodic_config( - PushMetricExporterConfig( - otlp_http=OtlpHttpMetricExporterConfig( + { + "otlp_http": OtlpHttpMetricExporterConfig( endpoint="http://localhost:4318" ) - ) + } ) create_meter_provider(config) @@ -276,7 +271,7 @@ def test_otlp_http_created_with_endpoint(self): def test_otlp_grpc_missing_package_raises(self): config = self._make_periodic_config( - PushMetricExporterConfig(otlp_grpc=OtlpGrpcMetricExporterConfig()) + {"otlp_grpc": OtlpGrpcMetricExporterConfig()} ) with patch.dict( sys.modules, @@ -302,7 +297,7 @@ def test_no_reader_type_raises(self): create_meter_provider(config) def test_no_exporter_type_raises(self): - config = self._make_periodic_config(PushMetricExporterConfig()) + config = self._make_periodic_config({}) with self.assertRaises(ConfigurationError): create_meter_provider(config) @@ -331,16 +326,12 @@ def test_multiple_readers(self): readers=[ MetricReaderConfig( periodic=PeriodicMetricReaderConfig( - exporter=PushMetricExporterConfig( - console=ConsoleMetricExporterConfig() - ) + exporter={"console": ConsoleMetricExporterConfig()} ) ), MetricReaderConfig( periodic=PeriodicMetricReaderConfig( - exporter=PushMetricExporterConfig( - console=ConsoleMetricExporterConfig() - ) + exporter={"console": ConsoleMetricExporterConfig()} ) ), ] @@ -356,12 +347,12 @@ def _make_console_config(temporality=None, histogram_agg=None): readers=[ MetricReaderConfig( periodic=PeriodicMetricReaderConfig( - exporter=PushMetricExporterConfig( - console=ConsoleMetricExporterConfig( + exporter={ + "console": ConsoleMetricExporterConfig( temporality_preference=temporality, default_histogram_aggregation=histogram_agg, ) - ) + } ) ) ] diff --git a/opentelemetry-sdk/tests/_configuration/test_tracer_provider.py b/opentelemetry-sdk/tests/_configuration/test_tracer_provider.py index 4d0f4e9c8d..1ed4b5d5cd 100644 --- a/opentelemetry-sdk/tests/_configuration/test_tracer_provider.py +++ b/opentelemetry-sdk/tests/_configuration/test_tracer_provider.py @@ -43,9 +43,6 @@ from opentelemetry.sdk._configuration.models import ( SimpleSpanProcessor as SimpleSpanProcessorConfig, ) -from opentelemetry.sdk._configuration.models import ( - SpanExporter as SpanExporterConfig, -) from opentelemetry.sdk._configuration.models import ( SpanLimits as SpanLimitsConfig, ) @@ -248,7 +245,7 @@ def _make_simple_config(exporter_config): ) def test_console_exporter_batch(self): - config = self._make_batch_config(SpanExporterConfig(console={})) + config = self._make_batch_config({"console": {}}) provider = create_tracer_provider(config) procs = provider._active_span_processor._span_processors self.assertEqual(len(procs), 1) @@ -256,7 +253,7 @@ def test_console_exporter_batch(self): self.assertIsInstance(procs[0].span_exporter, ConsoleSpanExporter) def test_console_exporter_simple(self): - config = self._make_simple_config(SpanExporterConfig(console={})) + config = self._make_simple_config({"console": {}}) provider = create_tracer_provider(config) procs = provider._active_span_processor._span_processors self.assertIsInstance(procs[0], SimpleSpanProcessor) @@ -264,7 +261,7 @@ def test_console_exporter_simple(self): def test_otlp_http_missing_package_raises(self): config = self._make_batch_config( - SpanExporterConfig(otlp_http=OtlpHttpExporterConfig()) + {"otlp_http": OtlpHttpExporterConfig()} ) with patch.dict( sys.modules, @@ -294,11 +291,11 @@ def test_otlp_http_created_with_endpoint(self): }, ): config = self._make_batch_config( - SpanExporterConfig( - otlp_http=OtlpHttpExporterConfig( + { + "otlp_http": OtlpHttpExporterConfig( endpoint="http://localhost:4318" ) - ) + } ) create_tracer_provider(config) @@ -323,11 +320,11 @@ def test_otlp_http_headers_list(self): }, ): config = self._make_batch_config( - SpanExporterConfig( - otlp_http=OtlpHttpExporterConfig( + { + "otlp_http": OtlpHttpExporterConfig( headers_list="x-api-key=secret,env=prod" ) - ) + } ) create_tracer_provider(config) @@ -338,7 +335,7 @@ def test_otlp_http_headers_list(self): def test_otlp_grpc_missing_package_raises(self): config = self._make_batch_config( - SpanExporterConfig(otlp_grpc=OtlpGrpcExporterConfig()) + {"otlp_grpc": OtlpGrpcExporterConfig()} ) with patch.dict( sys.modules, @@ -357,7 +354,7 @@ def test_no_processor_type_raises(self): create_tracer_provider(config) def test_no_exporter_type_raises(self): - config = self._make_batch_config(SpanExporterConfig()) + config = self._make_batch_config({}) with self.assertRaises(ConfigurationError): create_tracer_provider(config)