From f63076049dccd1f15e04600ae77e2894fb74793b Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Tue, 10 Nov 2020 17:34:57 -0600 Subject: [PATCH] Add fields property Fixes #1104 --- .../baggage/propagation/__init__.py | 9 +++++ .../opentelemetry/propagators/composite.py | 15 ++++++++ .../trace/propagation/textmap.py | 12 ++++++ .../trace/propagation/tracecontext.py | 11 ++++++ .../tests/baggage/test_baggage_propagation.py | 17 ++++++++- .../tests/propagators/test_composite.py | 37 ++++++++++++++++++- .../sdk/trace/propagation/b3_format.py | 9 +++++ .../tests/trace/propagation/test_b3_format.py | 19 ++++++++++ 8 files changed, 126 insertions(+), 3 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/baggage/propagation/__init__.py b/opentelemetry-api/src/opentelemetry/baggage/propagation/__init__.py index 70e73a3b23b..bcefc652348 100644 --- a/opentelemetry-api/src/opentelemetry/baggage/propagation/__init__.py +++ b/opentelemetry-api/src/opentelemetry/baggage/propagation/__init__.py @@ -87,6 +87,15 @@ def inject( baggage_string = _format_baggage(baggage_entries) set_in_carrier(carrier, self._BAGGAGE_HEADER_NAME, baggage_string) + @property + def fields(self) -> typing.Set[str]: + """Returns a set with the fields set in `inject`. + + See + `opentelemetry.trace.propagation.textmap.TextMapPropagator.fields` + """ + return {self._BAGGAGE_HEADER_NAME} + def _format_baggage(baggage_entries: typing.Mapping[str, object]) -> str: return ",".join( diff --git a/opentelemetry-api/src/opentelemetry/propagators/composite.py b/opentelemetry-api/src/opentelemetry/propagators/composite.py index 441098ec1f0..fde42d9373b 100644 --- a/opentelemetry-api/src/opentelemetry/propagators/composite.py +++ b/opentelemetry-api/src/opentelemetry/propagators/composite.py @@ -65,3 +65,18 @@ def inject( """ for propagator in self._propagators: propagator.inject(set_in_carrier, carrier, context) + + @property + def fields(self) -> typing.Set[str]: + """Returns a set with the fields set in `inject`. + + See + `opentelemetry.trace.propagation.textmap.TextMapPropagator.fields` + """ + composite_fields = set() + + for propagator in self._propagators: + for field in propagator.fields: + composite_fields.add(field) + + return composite_fields diff --git a/opentelemetry-api/src/opentelemetry/trace/propagation/textmap.py b/opentelemetry-api/src/opentelemetry/trace/propagation/textmap.py index ec505135bec..1a1d68f61f4 100644 --- a/opentelemetry-api/src/opentelemetry/trace/propagation/textmap.py +++ b/opentelemetry-api/src/opentelemetry/trace/propagation/textmap.py @@ -129,3 +129,15 @@ def inject( context if not set. """ + + @abc.abstractmethod + def fields(self) -> typing.Set[str]: + """ + Gets the fields set in the carrier by the `inject` method. + + If the carrier is reused, its fields that correspond with the ones + present in this attribute should be deleted before calling `inject`. + + Returns: + A set with the fields set in `inject`. + """ diff --git a/opentelemetry-api/src/opentelemetry/trace/propagation/tracecontext.py b/opentelemetry-api/src/opentelemetry/trace/propagation/tracecontext.py index 57933ef9c41..6f7a9adb9cc 100644 --- a/opentelemetry-api/src/opentelemetry/trace/propagation/tracecontext.py +++ b/opentelemetry-api/src/opentelemetry/trace/propagation/tracecontext.py @@ -132,6 +132,17 @@ def inject( carrier, self._TRACESTATE_HEADER_NAME, tracestate_string ) + @property + def fields(self) -> typing.Set[str]: + """Returns a set with the fields set in `inject`. + + See + `opentelemetry.trace.propagation.textmap.TextMapPropagator.fields` + """ + return { + self._TRACEPARENT_HEADER_NAME, self._TRACESTATE_HEADER_NAME + } + def _parse_tracestate(header_list: typing.List[str]) -> trace.TraceState: """Parse one or more w3c tracestate header into a TraceState. diff --git a/opentelemetry-api/tests/baggage/test_baggage_propagation.py b/opentelemetry-api/tests/baggage/test_baggage_propagation.py index 4c7b3de215a..91fac08cb99 100644 --- a/opentelemetry-api/tests/baggage/test_baggage_propagation.py +++ b/opentelemetry-api/tests/baggage/test_baggage_propagation.py @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # -import typing import unittest +from unittest.mock import patch, Mock from opentelemetry import baggage from opentelemetry.baggage.propagation import BaggagePropagator @@ -142,3 +142,18 @@ def test_inject_non_string_values(self): self.assertIn("key1=True", output) self.assertIn("key2=123", output) self.assertIn("key3=123.567", output) + + @patch("opentelemetry.baggage.propagation.baggage") + @patch("opentelemetry.baggage.propagation._format_baggage") + def test_fields(self, mock_format_baggage, mock_baggage): + + mock_set_in_carrier = Mock() + + self.propagator.inject(mock_set_in_carrier, {}) + + inject_fields = set() + + for mock_call in mock_set_in_carrier.mock_calls: + inject_fields.add(mock_call.args[1]) + + self.assertEqual(inject_fields, self.propagator.fields) diff --git a/opentelemetry-api/tests/propagators/test_composite.py b/opentelemetry-api/tests/propagators/test_composite.py index 8c61b6dc1fa..ae6e031242b 100644 --- a/opentelemetry-api/tests/propagators/test_composite.py +++ b/opentelemetry-api/tests/propagators/test_composite.py @@ -26,6 +26,8 @@ def get_as_list(dict_object, key): def mock_inject(name, value="data"): def wrapped(setter, carrier=None, context=None): carrier[name] = value + setter(None, "inject_field_{}_0".format(name), None) + setter(None, "inject_field_{}_1".format(name), None) return wrapped @@ -39,18 +41,29 @@ def wrapped(getter, carrier=None, context=None): return wrapped +def mock_fields(name): + return { + "inject_field_{}_0".format(name), "inject_field_{}_1".format(name) + } + + class TestCompositePropagator(unittest.TestCase): @classmethod def setUpClass(cls): cls.mock_propagator_0 = Mock( - inject=mock_inject("mock-0"), extract=mock_extract("mock-0") + inject=mock_inject("mock-0"), + extract=mock_extract("mock-0"), + fields=mock_fields("mock-0"), ) cls.mock_propagator_1 = Mock( - inject=mock_inject("mock-1"), extract=mock_extract("mock-1") + inject=mock_inject("mock-1"), + extract=mock_extract("mock-1"), + fields=mock_fields("mock-1"), ) cls.mock_propagator_2 = Mock( inject=mock_inject("mock-0", value="data2"), extract=mock_extract("mock-0", value="context2"), + fields=mock_fields("mock-0"), ) def test_no_propagators(self): @@ -105,3 +118,23 @@ def test_multiple_propagators_same_key(self): get_as_list, carrier=new_carrier, context={} ) self.assertEqual(context, {"mock-0": "context2"}) + + def test_fields(self): + propagator = CompositeHTTPPropagator( + [ + self.mock_propagator_0, + self.mock_propagator_1, + self.mock_propagator_2 + ] + ) + + mock_set_in_carrier = Mock() + + propagator.inject(mock_set_in_carrier, {}) + + inject_fields = set() + + for mock_call in mock_set_in_carrier.mock_calls: + inject_fields.add(mock_call.args[1]) + + self.assertEqual(inject_fields, propagator.fields) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/propagation/b3_format.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/propagation/b3_format.py index c629d107d36..7f59ff417b7 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/propagation/b3_format.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/propagation/b3_format.py @@ -152,6 +152,15 @@ def inject( ) set_in_carrier(carrier, self.SAMPLED_KEY, "1" if sampled else "0") + @property + def fields(self) -> typing.Set[str]: + return { + self.TRACE_ID_KEY, + self.SPAN_ID_KEY, + self.PARENT_SPAN_ID_KEY, + self.SAMPLED_KEY, + } + def format_trace_id(trace_id: int) -> str: """Format the trace id according to b3 specification.""" diff --git a/opentelemetry-sdk/tests/trace/propagation/test_b3_format.py b/opentelemetry-sdk/tests/trace/propagation/test_b3_format.py index 79c4618aee7..49cca025b04 100644 --- a/opentelemetry-sdk/tests/trace/propagation/test_b3_format.py +++ b/opentelemetry-sdk/tests/trace/propagation/test_b3_format.py @@ -14,6 +14,7 @@ import unittest from unittest.mock import patch +from unittest.mock import Mock import opentelemetry.sdk.trace as trace import opentelemetry.sdk.trace.propagation.b3_format as b3_format @@ -329,3 +330,21 @@ def setter(carrier, key, value): ctx = FORMAT.extract(CarrierGetter(), {}) FORMAT.inject(setter, {}, ctx) + + def test_fields(self): + """Make sure the fields attribute returns the fields used in inject""" + + tracer = trace.TracerProvider().get_tracer("sdk_tracer_provider") + + mock_set_in_carrier = Mock() + + with tracer.start_as_current_span("parent"): + with tracer.start_as_current_span("child"): + FORMAT.inject(mock_set_in_carrier, {}) + + inject_fields = set() + + for call in mock_set_in_carrier.mock_calls: + inject_fields.add(call.args[1]) + + self.assertEqual(FORMAT.fields, inject_fields)