From 90d74001984ec69b8d129201d8c69d6ca0a708e0 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Wed, 23 Sep 2020 16:54:25 -0600 Subject: [PATCH] Add support for OTEL_PROPAGATORS (#1123) Fixes #1058 --- opentelemetry-api/CHANGELOG.md | 2 + opentelemetry-api/setup.cfg | 3 + .../src/opentelemetry/propagators/__init__.py | 49 +++++++++-- .../tests/configuration/test_configuration.py | 7 +- .../tests/propagators/test_propagators.py | 83 +++++++++++++++++++ opentelemetry-sdk/setup.cfg | 2 + 6 files changed, 135 insertions(+), 11 deletions(-) create mode 100644 opentelemetry-api/tests/propagators/test_propagators.py diff --git a/opentelemetry-api/CHANGELOG.md b/opentelemetry-api/CHANGELOG.md index c56919b832..3d0bcd8005 100644 --- a/opentelemetry-api/CHANGELOG.md +++ b/opentelemetry-api/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +- Add support for `OTEL_PROPAGATORS` + ([#1123](https://github.com/open-telemetry/opentelemetry-python/pull/1123)) - Store `int`s as `int`s in the global Configuration object ([#1118](https://github.com/open-telemetry/opentelemetry-python/pull/1118)) diff --git a/opentelemetry-api/setup.cfg b/opentelemetry-api/setup.cfg index 9435d92f19..4ad44c6451 100644 --- a/opentelemetry-api/setup.cfg +++ b/opentelemetry-api/setup.cfg @@ -55,6 +55,9 @@ opentelemetry_meter_provider = default_meter_provider = opentelemetry.metrics:DefaultMeterProvider opentelemetry_tracer_provider = default_tracer_provider = opentelemetry.trace:DefaultTracerProvider +opentelemetry_propagator = + tracecontext = opentelemetry.trace.propagation.tracecontext:TraceContextTextMapPropagator + baggage = opentelemetry.baggage.propagation:BaggagePropagator [options.extras_require] test = diff --git a/opentelemetry-api/src/opentelemetry/propagators/__init__.py b/opentelemetry-api/src/opentelemetry/propagators/__init__.py index f34e3c588b..c274b19f8a 100644 --- a/opentelemetry-api/src/opentelemetry/propagators/__init__.py +++ b/opentelemetry-api/src/opentelemetry/propagators/__init__.py @@ -15,6 +15,21 @@ """ API for propagation of context. +The propagators for the +``opentelemetry.propagators.composite.CompositeHTTPPropagator`` can be defined +via configuration in the ``OTEL_PROPAGATORS`` environment variable. This +variable should be set to a comma-separated string of names of values for the +``opentelemetry_propagator`` entry point. For example, setting +``OTEL_PROPAGATORS`` to ``tracecontext,baggage`` (which is the default value) +would instantiate +``opentelemetry.propagators.composite.CompositeHTTPPropagator`` with 2 +propagators, one of type +``opentelemetry.trace.propagation.tracecontext.TraceContextTextMapPropagator`` +and other of type ``opentelemetry.baggage.propagation.BaggagePropagator``. +Notice that these propagator classes are defined as +``opentelemetry_propagator`` entry points in the ``setup.cfg`` file of +``opentelemetry``. + Example:: import flask @@ -54,14 +69,16 @@ def example_route(): """ import typing +from logging import getLogger + +from pkg_resources import iter_entry_points -from opentelemetry.baggage.propagation import BaggagePropagator +from opentelemetry.configuration import Configuration from opentelemetry.context.context import Context from opentelemetry.propagators import composite from opentelemetry.trace.propagation import textmap -from opentelemetry.trace.propagation.tracecontext import ( - TraceContextTextMapPropagator, -) + +logger = getLogger(__name__) def extract( @@ -104,9 +121,25 @@ def inject( get_global_textmap().inject(set_in_carrier, carrier, context) -_HTTP_TEXT_FORMAT = composite.CompositeHTTPPropagator( - [TraceContextTextMapPropagator(), BaggagePropagator()], -) # type: textmap.TextMapPropagator +try: + + propagators = [] + + for propagator in ( # type: ignore + Configuration().get("PROPAGATORS", "tracecontext,baggage").split(",") # type: ignore + ): + + propagators.append( # type: ignore + next( # type: ignore + iter_entry_points("opentelemetry_propagator", propagator) # type: ignore + ).load()() + ) + +except Exception: # pylint: disable=broad-except + logger.exception("Failed to load configured propagators") + raise + +_HTTP_TEXT_FORMAT = composite.CompositeHTTPPropagator(propagators) # type: ignore def get_global_textmap() -> textmap.TextMapPropagator: @@ -115,4 +148,4 @@ def get_global_textmap() -> textmap.TextMapPropagator: def set_global_textmap(http_text_format: textmap.TextMapPropagator,) -> None: global _HTTP_TEXT_FORMAT # pylint:disable=global-statement - _HTTP_TEXT_FORMAT = http_text_format + _HTTP_TEXT_FORMAT = http_text_format # type: ignore diff --git a/opentelemetry-api/tests/configuration/test_configuration.py b/opentelemetry-api/tests/configuration/test_configuration.py index 45ed9627d8..608a96c96d 100644 --- a/opentelemetry-api/tests/configuration/test_configuration.py +++ b/opentelemetry-api/tests/configuration/test_configuration.py @@ -20,9 +20,10 @@ class TestConfiguration(TestCase): - def tearDown(self) -> None: - # This call resets the attributes of the Configuration class so that - # each test is executed in the same conditions. + + # These calls reset the attributes of the Configuration class so that each + # test is executed in the same conditions. + def setUp(self) -> None: Configuration._reset() def test_singleton(self) -> None: diff --git a/opentelemetry-api/tests/propagators/test_propagators.py b/opentelemetry-api/tests/propagators/test_propagators.py new file mode 100644 index 0000000000..70f4999dff --- /dev/null +++ b/opentelemetry-api/tests/propagators/test_propagators.py @@ -0,0 +1,83 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from importlib import reload +from os import environ +from unittest import TestCase +from unittest.mock import Mock, patch + +from opentelemetry.baggage.propagation import BaggagePropagator +from opentelemetry.configuration import Configuration +from opentelemetry.trace.propagation.tracecontext import ( + TraceContextTextMapPropagator, +) + + +class TestPropagators(TestCase): + @patch("opentelemetry.propagators.composite.CompositeHTTPPropagator") + def test_default_composite_propagators(self, mock_compositehttppropagator): + def test_propagators(propagators): + + propagators = {propagator.__class__ for propagator in propagators} + + self.assertEqual(len(propagators), 2) + self.assertEqual( + propagators, {TraceContextTextMapPropagator, BaggagePropagator} + ) + + mock_compositehttppropagator.configure_mock( + **{"side_effect": test_propagators} + ) + + import opentelemetry.propagators + + reload(opentelemetry.propagators) + + @patch.dict(environ, {"OTEL_PROPAGATORS": "a,b,c"}) + @patch("opentelemetry.propagators.composite.CompositeHTTPPropagator") + @patch("pkg_resources.iter_entry_points") + def test_non_default_propagators( + self, mock_iter_entry_points, mock_compositehttppropagator + ): + + Configuration._reset() + + def iter_entry_points_mock(_, propagator): + return iter( + [ + Mock( + **{ + "load.side_effect": [ + Mock(**{"side_effect": [propagator]}) + ] + } + ) + ] + ) + + mock_iter_entry_points.configure_mock( + **{"side_effect": iter_entry_points_mock} + ) + + def test_propagators(propagators): + + self.assertEqual(propagators, ["a", "b", "c"]) + + mock_compositehttppropagator.configure_mock( + **{"side_effect": test_propagators} + ) + + import opentelemetry.propagators + + reload(opentelemetry.propagators) diff --git a/opentelemetry-sdk/setup.cfg b/opentelemetry-sdk/setup.cfg index dd7c9aee35..92a7853ae5 100644 --- a/opentelemetry-sdk/setup.cfg +++ b/opentelemetry-sdk/setup.cfg @@ -51,6 +51,8 @@ opentelemetry_meter_provider = sdk_meter_provider = opentelemetry.sdk.metrics:MeterProvider opentelemetry_tracer_provider = sdk_tracer_provider = opentelemetry.sdk.trace:TracerProvider +opentelemetry_propagator = + b3 = opentelemetry.sdk.trace.propagation.b3_format.B3Format [options.extras_require] test =