From 0ef86ee87f6308ca9755785011deeade39b2b370 Mon Sep 17 00:00:00 2001 From: Ron Yishai Date: Sun, 28 Aug 2022 16:07:28 +0300 Subject: [PATCH 1/7] Adding support for setting OTLP exporter protocol by env vars --- .../sdk/_configuration/__init__.py | 71 ++++++++++++++----- .../sdk/environment_variables.py | 28 ++++++-- opentelemetry-sdk/tests/test_configurator.py | 27 ++++--- 3 files changed, 93 insertions(+), 33 deletions(-) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py index 25a6580f1ba..30fca30f696 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py @@ -22,15 +22,11 @@ from abc import ABC, abstractmethod from os import environ from typing import Dict, Optional, Sequence, Tuple, Type +from typing_extensions import Literal from pkg_resources import iter_entry_points -from opentelemetry.environment_variables import ( - OTEL_LOGS_EXPORTER, - OTEL_METRICS_EXPORTER, - OTEL_PYTHON_ID_GENERATOR, - OTEL_TRACES_EXPORTER, -) +from opentelemetry.environment_variables import OTEL_PYTHON_ID_GENERATOR from opentelemetry.metrics import set_meter_provider from opentelemetry.sdk._logs import ( LogEmitterProvider, @@ -40,6 +36,7 @@ from opentelemetry.sdk._logs.export import BatchLogProcessor, LogExporter from opentelemetry.sdk.environment_variables import ( _OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED, + OTEL_EXPORTER_OTLP_PROTOCOL, ) from opentelemetry.sdk.metrics import MeterProvider from opentelemetry.sdk.metrics.export import ( @@ -55,26 +52,66 @@ _EXPORTER_OTLP = "otlp" _EXPORTER_OTLP_PROTO_GRPC = "otlp_proto_grpc" +_EXPORTER_OTLP_PROTO_HTTP = "otlp_proto_http" _RANDOM_ID_GENERATOR = "random" _DEFAULT_ID_GENERATOR = _RANDOM_ID_GENERATOR +_logger = logging.getLogger(__name__) + def _get_id_generator() -> str: return environ.get(OTEL_PYTHON_ID_GENERATOR, _DEFAULT_ID_GENERATOR) -def _get_exporter_names(names: str) -> Sequence[str]: - exporters = set() +def _get_otlp_exporter_entry_point(otlp_protocol: Optional[str]) -> Literal[ + _EXPORTER_OTLP_PROTO_GRPC, + _EXPORTER_OTLP_PROTO_HTTP +]: + if otlp_protocol == "grpc": + return _EXPORTER_OTLP_PROTO_GRPC + elif otlp_protocol == "http/protobuf": + return _EXPORTER_OTLP_PROTO_HTTP + + raise RuntimeError(f"Unsupported OTLP protocol '{otlp_protocol}' is configured") + + +def _get_exporter_entry_point(exporter_name: str, telemetry_type: Literal["traces", "metrics", "logs"]): + if exporter_name not in (_EXPORTER_OTLP, _EXPORTER_OTLP_PROTO_GRPC, _EXPORTER_OTLP_PROTO_HTTP): + return exporter_name + + exporter_entry_point = None + + # Check the specific env var + otlp_protocol_var = environ.get(f"OTEL_EXPORTER_OTLP_{telemetry_type.upper()}_PROTOCOL") + if otlp_protocol_var: + exporter_entry_point = _get_otlp_exporter_entry_point(otlp_protocol_var) + else: + # Check the general env var + otlp_protocol_var = environ.get(OTEL_EXPORTER_OTLP_PROTOCOL) + if otlp_protocol_var: + exporter_entry_point = _get_otlp_exporter_entry_point(otlp_protocol_var) + + if exporter_name == _EXPORTER_OTLP: + return exporter_entry_point if exporter_entry_point else _EXPORTER_OTLP_PROTO_GRPC + + if exporter_name != exporter_entry_point: + # Env vars conflict + _logger.warning( + f"Conflicting values for {telemetry_type.lower()} OTLP exporter protocol, using {exporter_name}" + ) + + return exporter_name - if names and names.lower().strip() != "none": - exporters.update({_exporter.strip() for _exporter in names.split(",")}) - if _EXPORTER_OTLP in exporters: - exporters.remove(_EXPORTER_OTLP) - exporters.add(_EXPORTER_OTLP_PROTO_GRPC) +def _get_exporter_names(telemetry_type: Literal["traces", "metrics", "logs"]) -> Sequence[str]: + names = environ.get(f"OTEL_{telemetry_type.upper()}_EXPORTER") + if not names or names.lower().strip() == "none": + return [] - return list(exporters) + return [ + _get_exporter_entry_point(_exporter.strip(), telemetry_type) for _exporter in names.split(",") + ] def _init_tracing( @@ -232,9 +269,9 @@ def _import_id_generator(id_generator_name: str) -> IdGenerator: def _initialize_components(auto_instrumentation_version): trace_exporters, metric_exporters, log_exporters = _import_exporters( - _get_exporter_names(environ.get(OTEL_TRACES_EXPORTER)), - _get_exporter_names(environ.get(OTEL_METRICS_EXPORTER)), - _get_exporter_names(environ.get(OTEL_LOGS_EXPORTER)), + _get_exporter_names("traces"), + _get_exporter_names("metrics"), + _get_exporter_names("logs"), ) id_generator_name = _get_id_generator() id_generator = _import_id_generator(id_generator_name) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/environment_variables.py b/opentelemetry-sdk/src/opentelemetry/sdk/environment_variables.py index 384d563d259..ad635e89d58 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/environment_variables.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/environment_variables.py @@ -234,6 +234,27 @@ OTLP exporter. """ +OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL" +""" +.. envvar:: OTEL_EXPORTER_OTLP_TRACES_PROTOCOL + +The :envvar:`OTEL_EXPORTER_OTLP_TRACES_PROTOCOL` represents the the transport protocol for spans. +""" + +OTEL_EXPORTER_OTLP_METRICS_PROTOCOL = "OTEL_EXPORTER_OTLP_METRICS_PROTOCOL" +""" +.. envvar:: OTEL_EXPORTER_OTLP_METRICS_PROTOCOL + +The :envvar:`OTEL_EXPORTER_OTLP_TRACES_PROTOCOL` represents the the transport protocol for metrics. +""" + +OTEL_EXPORTER_OTLP_LOGS_PROTOCOL = "OTEL_EXPORTER_OTLP_LOGS_PROTOCOL" +""" +.. envvar:: OTEL_EXPORTER_OTLP_LOGS_PROTOCOL + +The :envvar:`OTEL_EXPORTER_OTLP_TRACES_PROTOCOL` represents the the transport protocol for logs. +""" + OTEL_EXPORTER_OTLP_CERTIFICATE = "OTEL_EXPORTER_OTLP_CERTIFICATE" """ .. envvar:: OTEL_EXPORTER_OTLP_CERTIFICATE @@ -314,13 +335,6 @@ A scheme of https indicates a secure connection and takes precedence over this configuration setting. """ -OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL" -""" -.. envvar:: OTEL_EXPORTER_OTLP_TRACES_PROTOCOL - -The :envvar:`OTEL_EXPORTER_OTLP_TRACES_PROTOCOL` represents the the transport protocol for spans. -""" - OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE = "OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE" """ .. envvar:: OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE diff --git a/opentelemetry-sdk/tests/test_configurator.py b/opentelemetry-sdk/tests/test_configurator.py index 6afbf4d2953..44ed19948d6 100644 --- a/opentelemetry-sdk/tests/test_configurator.py +++ b/opentelemetry-sdk/tests/test_configurator.py @@ -413,25 +413,34 @@ def test_metrics_init_exporter(self): class TestExporterNames(TestCase): - def test_otlp_exporter_overwrite(self): - for exporter in [_EXPORTER_OTLP, _EXPORTER_OTLP_PROTO_GRPC]: - self.assertEqual( - _get_exporter_names(exporter), [_EXPORTER_OTLP_PROTO_GRPC] - ) + @patch.dict(environ, {"OTEL_TRACES_EXPORTER": _EXPORTER_OTLP}) + def test_otlp_exporter(self): + self.assertEqual( + sorted(_get_exporter_names("traces")), [_EXPORTER_OTLP_PROTO_GRPC] + ) + + @patch.dict(environ, {"OTEL_TRACES_EXPORTER": _EXPORTER_OTLP_PROTO_GRPC}) + def test_otlp_grpc_exporter(self): + self.assertEqual( + sorted(_get_exporter_names("traces")), [_EXPORTER_OTLP_PROTO_GRPC] + ) + @patch.dict(environ, {"OTEL_TRACES_EXPORTER": "jaeger,zipkin"}) def test_multiple_exporters(self): self.assertEqual( - sorted(_get_exporter_names("jaeger,zipkin")), ["jaeger", "zipkin"] + sorted(_get_exporter_names("traces")), ["jaeger", "zipkin"] ) + @patch.dict(environ, {"OTEL_TRACES_EXPORTER": "none"}) def test_none_exporters(self): - self.assertEqual(sorted(_get_exporter_names("none")), []) + self.assertEqual(sorted(_get_exporter_names("traces")), []) def test_no_exporters(self): - self.assertEqual(sorted(_get_exporter_names(None)), []) + self.assertEqual(sorted(_get_exporter_names("traces")), []) + @patch.dict(environ, {"OTEL_TRACES_EXPORTER": ""}) def test_empty_exporters(self): - self.assertEqual(sorted(_get_exporter_names("")), []) + self.assertEqual(sorted(_get_exporter_names("traces")), []) class TestImportExporters(TestCase): From b1302e153a1b45c55c4875f4d189c792288c37a4 Mon Sep 17 00:00:00 2001 From: Ron Yishai Date: Sun, 28 Aug 2022 16:51:10 +0300 Subject: [PATCH 2/7] lint --- .../sdk/_configuration/__init__.py | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py index 30fca30f696..580e0c3cb94 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py @@ -22,9 +22,9 @@ from abc import ABC, abstractmethod from os import environ from typing import Dict, Optional, Sequence, Tuple, Type -from typing_extensions import Literal from pkg_resources import iter_entry_points +from typing_extensions import Literal from opentelemetry.environment_variables import OTEL_PYTHON_ID_GENERATOR from opentelemetry.metrics import set_meter_provider @@ -64,36 +64,53 @@ def _get_id_generator() -> str: return environ.get(OTEL_PYTHON_ID_GENERATOR, _DEFAULT_ID_GENERATOR) -def _get_otlp_exporter_entry_point(otlp_protocol: Optional[str]) -> Literal[ - _EXPORTER_OTLP_PROTO_GRPC, - _EXPORTER_OTLP_PROTO_HTTP -]: +def _get_otlp_exporter_entry_point( + otlp_protocol: Optional[str], +) -> Literal[_EXPORTER_OTLP_PROTO_GRPC, _EXPORTER_OTLP_PROTO_HTTP]: if otlp_protocol == "grpc": return _EXPORTER_OTLP_PROTO_GRPC elif otlp_protocol == "http/protobuf": return _EXPORTER_OTLP_PROTO_HTTP - raise RuntimeError(f"Unsupported OTLP protocol '{otlp_protocol}' is configured") + raise RuntimeError( + f"Unsupported OTLP protocol '{otlp_protocol}' is configured" + ) -def _get_exporter_entry_point(exporter_name: str, telemetry_type: Literal["traces", "metrics", "logs"]): - if exporter_name not in (_EXPORTER_OTLP, _EXPORTER_OTLP_PROTO_GRPC, _EXPORTER_OTLP_PROTO_HTTP): +def _get_exporter_entry_point( + exporter_name: str, telemetry_type: Literal["traces", "metrics", "logs"] +): + if exporter_name not in ( + _EXPORTER_OTLP, + _EXPORTER_OTLP_PROTO_GRPC, + _EXPORTER_OTLP_PROTO_HTTP, + ): return exporter_name exporter_entry_point = None # Check the specific env var - otlp_protocol_var = environ.get(f"OTEL_EXPORTER_OTLP_{telemetry_type.upper()}_PROTOCOL") + otlp_protocol_var = environ.get( + f"OTEL_EXPORTER_OTLP_{telemetry_type.upper()}_PROTOCOL" + ) if otlp_protocol_var: - exporter_entry_point = _get_otlp_exporter_entry_point(otlp_protocol_var) + exporter_entry_point = _get_otlp_exporter_entry_point( + otlp_protocol_var + ) else: # Check the general env var otlp_protocol_var = environ.get(OTEL_EXPORTER_OTLP_PROTOCOL) if otlp_protocol_var: - exporter_entry_point = _get_otlp_exporter_entry_point(otlp_protocol_var) + exporter_entry_point = _get_otlp_exporter_entry_point( + otlp_protocol_var + ) if exporter_name == _EXPORTER_OTLP: - return exporter_entry_point if exporter_entry_point else _EXPORTER_OTLP_PROTO_GRPC + return ( + exporter_entry_point + if exporter_entry_point + else _EXPORTER_OTLP_PROTO_GRPC + ) if exporter_name != exporter_entry_point: # Env vars conflict @@ -104,13 +121,16 @@ def _get_exporter_entry_point(exporter_name: str, telemetry_type: Literal["trace return exporter_name -def _get_exporter_names(telemetry_type: Literal["traces", "metrics", "logs"]) -> Sequence[str]: +def _get_exporter_names( + telemetry_type: Literal["traces", "metrics", "logs"] +) -> Sequence[str]: names = environ.get(f"OTEL_{telemetry_type.upper()}_EXPORTER") if not names or names.lower().strip() == "none": return [] return [ - _get_exporter_entry_point(_exporter.strip(), telemetry_type) for _exporter in names.split(",") + _get_exporter_entry_point(_exporter.strip(), telemetry_type) + for _exporter in names.split(",") ] From 3315e8f994e3566e3fe63f1a605966b2925826ff Mon Sep 17 00:00:00 2001 From: Ron Yishai Date: Mon, 29 Aug 2022 17:45:20 +0300 Subject: [PATCH 3/7] lint+changelog --- CHANGELOG.md | 3 +++ .../src/opentelemetry/sdk/_configuration/__init__.py | 7 ++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4af2ef82ec..307d9ae91ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#2870](https://github.com/open-telemetry/opentelemetry-python/pull/2870)) - Fix: Remove `LogEmitter.flush()` to align with OTel Log spec ([#2863](https://github.com/open-telemetry/opentelemetry-python/pull/2863)) +- Add support for setting OTLP export protocol with env vars, as defined in the + [specifications](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md#specify-protocol) + ([#2893](https://github.com/open-telemetry/opentelemetry-python/pull/2893)) ## [1.12.0-0.33b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.12.0) - 2022-08-08 diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py index 580e0c3cb94..313ff871447 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py @@ -69,9 +69,8 @@ def _get_otlp_exporter_entry_point( ) -> Literal[_EXPORTER_OTLP_PROTO_GRPC, _EXPORTER_OTLP_PROTO_HTTP]: if otlp_protocol == "grpc": return _EXPORTER_OTLP_PROTO_GRPC - elif otlp_protocol == "http/protobuf": + if otlp_protocol == "http/protobuf": return _EXPORTER_OTLP_PROTO_HTTP - raise RuntimeError( f"Unsupported OTLP protocol '{otlp_protocol}' is configured" ) @@ -115,7 +114,9 @@ def _get_exporter_entry_point( if exporter_name != exporter_entry_point: # Env vars conflict _logger.warning( - f"Conflicting values for {telemetry_type.lower()} OTLP exporter protocol, using {exporter_name}" + "Conflicting values for %s OTLP exporter protocol, using '%s'", + telemetry_type.lower(), + exporter_name, ) return exporter_name From efcd2e9ead63c659e431467fed5436dd36341fb4 Mon Sep 17 00:00:00 2001 From: Ron Yishai Date: Mon, 29 Aug 2022 21:07:21 +0300 Subject: [PATCH 4/7] tests --- .../sdk/_configuration/__init__.py | 7 +-- opentelemetry-sdk/tests/test_configurator.py | 53 +++++++++++++++++-- 2 files changed, 52 insertions(+), 8 deletions(-) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py index 313ff871447..0eac49f6972 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py @@ -88,7 +88,9 @@ def _get_exporter_entry_point( exporter_entry_point = None - # Check the specific env var + # Checking env vars for OTLP protocol (grpc/http). + # If grpc/http already specified by exporter_name, will only log a warning + # in case of a conflict. otlp_protocol_var = environ.get( f"OTEL_EXPORTER_OTLP_{telemetry_type.upper()}_PROTOCOL" ) @@ -111,8 +113,7 @@ def _get_exporter_entry_point( else _EXPORTER_OTLP_PROTO_GRPC ) - if exporter_name != exporter_entry_point: - # Env vars conflict + if exporter_entry_point and exporter_name != exporter_entry_point: _logger.warning( "Conflicting values for %s OTLP exporter protocol, using '%s'", telemetry_type.lower(), diff --git a/opentelemetry-sdk/tests/test_configurator.py b/opentelemetry-sdk/tests/test_configurator.py index 44ed19948d6..e5e43ed24ee 100644 --- a/opentelemetry-sdk/tests/test_configurator.py +++ b/opentelemetry-sdk/tests/test_configurator.py @@ -25,6 +25,7 @@ from opentelemetry.sdk._configuration import ( _EXPORTER_OTLP, _EXPORTER_OTLP_PROTO_GRPC, + _EXPORTER_OTLP_PROTO_HTTP, _get_exporter_names, _get_id_generator, _import_exporters, @@ -413,16 +414,58 @@ def test_metrics_init_exporter(self): class TestExporterNames(TestCase): - @patch.dict(environ, {"OTEL_TRACES_EXPORTER": _EXPORTER_OTLP}) + @patch.dict( + environ, + { + "OTEL_TRACES_EXPORTER": _EXPORTER_OTLP, + "OTEL_METRICS_EXPORTER": _EXPORTER_OTLP_PROTO_GRPC, + "OTEL_LOGS_EXPORTER": _EXPORTER_OTLP_PROTO_HTTP, + }, + ) def test_otlp_exporter(self): self.assertEqual( - sorted(_get_exporter_names("traces")), [_EXPORTER_OTLP_PROTO_GRPC] + _get_exporter_names("traces"), [_EXPORTER_OTLP_PROTO_GRPC] + ) + self.assertEqual( + _get_exporter_names("metrics"), [_EXPORTER_OTLP_PROTO_GRPC] + ) + self.assertEqual( + _get_exporter_names("logs"), [_EXPORTER_OTLP_PROTO_HTTP] + ) + + @patch.dict( + environ, + { + "OTEL_TRACES_EXPORTER": _EXPORTER_OTLP, + "OTEL_METRICS_EXPORTER": _EXPORTER_OTLP, + "OTEL_EXPORTER_OTLP_PROTOCOL": "http/protobuf", + "OTEL_EXPORTER_OTLP_METRICS_PROTOCOL": "grpc", + }, + ) + def test_otlp_custom_exporter(self): + self.assertEqual( + _get_exporter_names("traces"), [_EXPORTER_OTLP_PROTO_HTTP] + ) + self.assertEqual( + _get_exporter_names("metrics"), [_EXPORTER_OTLP_PROTO_GRPC] ) - @patch.dict(environ, {"OTEL_TRACES_EXPORTER": _EXPORTER_OTLP_PROTO_GRPC}) - def test_otlp_grpc_exporter(self): + @patch.dict( + environ, + { + "OTEL_TRACES_EXPORTER": _EXPORTER_OTLP_PROTO_HTTP, + "OTEL_METRICS_EXPORTER": _EXPORTER_OTLP_PROTO_GRPC, + "OTEL_EXPORTER_OTLP_PROTOCOL": "grpc", + "OTEL_EXPORTER_OTLP_METRICS_PROTOCOL": "http/protobuf", + }, + ) + def test_otlp_exporter_conflict(self): + # Verify that OTEL_*_EXPORTER is used + self.assertEqual( + _get_exporter_names("traces"), [_EXPORTER_OTLP_PROTO_HTTP] + ) self.assertEqual( - sorted(_get_exporter_names("traces")), [_EXPORTER_OTLP_PROTO_GRPC] + _get_exporter_names("metrics"), [_EXPORTER_OTLP_PROTO_GRPC] ) @patch.dict(environ, {"OTEL_TRACES_EXPORTER": "jaeger,zipkin"}) From e8022be6218603874e22f23e7c5af958eb21d299 Mon Sep 17 00:00:00 2001 From: Ron Yishai Date: Wed, 31 Aug 2022 11:56:20 +0300 Subject: [PATCH 5/7] CR fix --- .../sdk/_configuration/__init__.py | 94 +++++++++++-------- 1 file changed, 53 insertions(+), 41 deletions(-) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py index 0eac49f6972..2aa0e1473cf 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py @@ -26,7 +26,12 @@ from pkg_resources import iter_entry_points from typing_extensions import Literal -from opentelemetry.environment_variables import OTEL_PYTHON_ID_GENERATOR +from opentelemetry.environment_variables import ( + OTEL_LOGS_EXPORTER, + OTEL_METRICS_EXPORTER, + OTEL_PYTHON_ID_GENERATOR, + OTEL_TRACES_EXPORTER, +) from opentelemetry.metrics import set_meter_provider from opentelemetry.sdk._logs import ( LogEmitterProvider, @@ -36,7 +41,10 @@ from opentelemetry.sdk._logs.export import BatchLogProcessor, LogExporter from opentelemetry.sdk.environment_variables import ( _OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED, + OTEL_EXPORTER_OTLP_LOGS_PROTOCOL, + OTEL_EXPORTER_OTLP_METRICS_PROTOCOL, OTEL_EXPORTER_OTLP_PROTOCOL, + OTEL_EXPORTER_OTLP_TRACES_PROTOCOL, ) from opentelemetry.sdk.metrics import MeterProvider from opentelemetry.sdk.metrics.export import ( @@ -54,6 +62,23 @@ _EXPORTER_OTLP_PROTO_GRPC = "otlp_proto_grpc" _EXPORTER_OTLP_PROTO_HTTP = "otlp_proto_http" +_EXPORTER_BY_OTLP_PROTOCOL = { + "grpc": _EXPORTER_OTLP_PROTO_GRPC, + "http/protobuf": _EXPORTER_OTLP_PROTO_HTTP, +} + +_EXPORTER_ENV_BY_SIGNAL_TYPE = { + "traces": OTEL_TRACES_EXPORTER, + "metrics": OTEL_METRICS_EXPORTER, + "logs": OTEL_LOGS_EXPORTER, +} + +_PROTOCOL_ENV_BY_SIGNAL_TYPE = { + "traces": OTEL_EXPORTER_OTLP_TRACES_PROTOCOL, + "metrics": OTEL_EXPORTER_OTLP_METRICS_PROTOCOL, + "logs": OTEL_EXPORTER_OTLP_LOGS_PROTOCOL, +} + _RANDOM_ID_GENERATOR = "random" _DEFAULT_ID_GENERATOR = _RANDOM_ID_GENERATOR @@ -64,20 +89,8 @@ def _get_id_generator() -> str: return environ.get(OTEL_PYTHON_ID_GENERATOR, _DEFAULT_ID_GENERATOR) -def _get_otlp_exporter_entry_point( - otlp_protocol: Optional[str], -) -> Literal[_EXPORTER_OTLP_PROTO_GRPC, _EXPORTER_OTLP_PROTO_HTTP]: - if otlp_protocol == "grpc": - return _EXPORTER_OTLP_PROTO_GRPC - if otlp_protocol == "http/protobuf": - return _EXPORTER_OTLP_PROTO_HTTP - raise RuntimeError( - f"Unsupported OTLP protocol '{otlp_protocol}' is configured" - ) - - def _get_exporter_entry_point( - exporter_name: str, telemetry_type: Literal["traces", "metrics", "logs"] + exporter_name: str, signal_type: Literal["traces", "metrics", "logs"] ): if exporter_name not in ( _EXPORTER_OTLP, @@ -86,37 +99,35 @@ def _get_exporter_entry_point( ): return exporter_name - exporter_entry_point = None - # Checking env vars for OTLP protocol (grpc/http). - # If grpc/http already specified by exporter_name, will only log a warning - # in case of a conflict. - otlp_protocol_var = environ.get( - f"OTEL_EXPORTER_OTLP_{telemetry_type.upper()}_PROTOCOL" + protocol_env_var = _PROTOCOL_ENV_BY_SIGNAL_TYPE.get(signal_type) + if not protocol_env_var: + return exporter_name + + otlp_protocol = environ.get(protocol_env_var) or environ.get( + OTEL_EXPORTER_OTLP_PROTOCOL ) - if otlp_protocol_var: - exporter_entry_point = _get_otlp_exporter_entry_point( - otlp_protocol_var - ) - else: - # Check the general env var - otlp_protocol_var = environ.get(OTEL_EXPORTER_OTLP_PROTOCOL) - if otlp_protocol_var: - exporter_entry_point = _get_otlp_exporter_entry_point( - otlp_protocol_var - ) + if not otlp_protocol: + if exporter_name == _EXPORTER_OTLP: + return _EXPORTER_OTLP_PROTO_GRPC + return exporter_name if exporter_name == _EXPORTER_OTLP: - return ( - exporter_entry_point - if exporter_entry_point - else _EXPORTER_OTLP_PROTO_GRPC - ) + if otlp_protocol not in _EXPORTER_BY_OTLP_PROTOCOL: + # Invalid value was set by the env var + raise RuntimeError( + f"Unsupported OTLP protocol '{otlp_protocol}' is configured" + ) - if exporter_entry_point and exporter_name != exporter_entry_point: + return _EXPORTER_BY_OTLP_PROTOCOL[otlp_protocol] + + # grpc/http already specified by exporter_name, only add a warning in case + # of a conflict. + exporter_name_by_env = _EXPORTER_BY_OTLP_PROTOCOL.get(otlp_protocol) + if exporter_name_by_env and exporter_name != exporter_name_by_env: _logger.warning( "Conflicting values for %s OTLP exporter protocol, using '%s'", - telemetry_type.lower(), + signal_type, exporter_name, ) @@ -124,14 +135,15 @@ def _get_exporter_entry_point( def _get_exporter_names( - telemetry_type: Literal["traces", "metrics", "logs"] + signal_type: Literal["traces", "metrics", "logs"] ) -> Sequence[str]: - names = environ.get(f"OTEL_{telemetry_type.upper()}_EXPORTER") + names = environ.get(_EXPORTER_ENV_BY_SIGNAL_TYPE.get(signal_type, "")) + if not names or names.lower().strip() == "none": return [] return [ - _get_exporter_entry_point(_exporter.strip(), telemetry_type) + _get_exporter_entry_point(_exporter.strip(), signal_type) for _exporter in names.split(",") ] From ca80ebb3d519a272e7c89ef24e91b2ad4acd05bc Mon Sep 17 00:00:00 2001 From: Ron Yishai Date: Sun, 4 Sep 2022 15:40:34 +0300 Subject: [PATCH 6/7] CR fix --- .../sdk/_configuration/__init__.py | 11 +++++------ opentelemetry-sdk/tests/test_configurator.py | 19 ++++++++++++------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py index 2aa0e1473cf..0eed37793c1 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py @@ -100,18 +100,17 @@ def _get_exporter_entry_point( return exporter_name # Checking env vars for OTLP protocol (grpc/http). - protocol_env_var = _PROTOCOL_ENV_BY_SIGNAL_TYPE.get(signal_type) - if not protocol_env_var: - return exporter_name - - otlp_protocol = environ.get(protocol_env_var) or environ.get( - OTEL_EXPORTER_OTLP_PROTOCOL + otlp_protocol = ( + environ.get(_PROTOCOL_ENV_BY_SIGNAL_TYPE[signal_type]) or + environ.get(OTEL_EXPORTER_OTLP_PROTOCOL) ) if not otlp_protocol: if exporter_name == _EXPORTER_OTLP: return _EXPORTER_OTLP_PROTO_GRPC return exporter_name + otlp_protocol = otlp_protocol.strip() + if exporter_name == _EXPORTER_OTLP: if otlp_protocol not in _EXPORTER_BY_OTLP_PROTOCOL: # Invalid value was set by the env var diff --git a/opentelemetry-sdk/tests/test_configurator.py b/opentelemetry-sdk/tests/test_configurator.py index e5e43ed24ee..4aae8aa53be 100644 --- a/opentelemetry-sdk/tests/test_configurator.py +++ b/opentelemetry-sdk/tests/test_configurator.py @@ -460,13 +460,18 @@ def test_otlp_custom_exporter(self): }, ) def test_otlp_exporter_conflict(self): - # Verify that OTEL_*_EXPORTER is used - self.assertEqual( - _get_exporter_names("traces"), [_EXPORTER_OTLP_PROTO_HTTP] - ) - self.assertEqual( - _get_exporter_names("metrics"), [_EXPORTER_OTLP_PROTO_GRPC] - ) + # Verify that OTEL_*_EXPORTER is used, and a warning is logged + with self.assertLogs(level="WARNING") as logs_context: + self.assertEqual( + _get_exporter_names("traces"), [_EXPORTER_OTLP_PROTO_HTTP] + ) + assert len(logs_context.output) == 1 + + with self.assertLogs(level="WARNING") as logs_context: + self.assertEqual( + _get_exporter_names("metrics"), [_EXPORTER_OTLP_PROTO_GRPC] + ) + assert len(logs_context.output) == 1 @patch.dict(environ, {"OTEL_TRACES_EXPORTER": "jaeger,zipkin"}) def test_multiple_exporters(self): From b156f7157f18fc6e3858eb011374e24388f3a584 Mon Sep 17 00:00:00 2001 From: Ron Yishai Date: Sun, 4 Sep 2022 16:32:37 +0300 Subject: [PATCH 7/7] lint --- .../src/opentelemetry/sdk/_configuration/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py index 0eed37793c1..159d471900a 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py @@ -100,10 +100,10 @@ def _get_exporter_entry_point( return exporter_name # Checking env vars for OTLP protocol (grpc/http). - otlp_protocol = ( - environ.get(_PROTOCOL_ENV_BY_SIGNAL_TYPE[signal_type]) or - environ.get(OTEL_EXPORTER_OTLP_PROTOCOL) - ) + otlp_protocol = environ.get( + _PROTOCOL_ENV_BY_SIGNAL_TYPE[signal_type] + ) or environ.get(OTEL_EXPORTER_OTLP_PROTOCOL) + if not otlp_protocol: if exporter_name == _EXPORTER_OTLP: return _EXPORTER_OTLP_PROTO_GRPC