From a8dc9b39de7845a7e5a1cadf29adfe841b08ef07 Mon Sep 17 00:00:00 2001 From: Yusuke Tsutsumi Date: Thu, 15 Aug 2019 20:35:38 -0700 Subject: [PATCH 1/7] Changed to the propagator API Adding a UnifiedContext, composing DistributedContext and SpanContext. This will enable propagators to extract and inject values from either system, enabling more sophisticated schemes and standards to propagate data. This also removes the need for generics and propagators that only consume one or the other, requiring integrators to do extra work to wire propagators appropriately. Modifying the API of the propagators to consume the context as a mutable argument. By passing in the context rather than returning, this enables the chained use of propagators, allowing for situations such as supporting multiple trace propagation standards simulatenously. --- .../src/opentelemetry/context/__init__.py | 17 ++-- .../src/opentelemetry/context/base_context.py | 6 +- .../context/propagation/binaryformat.py | 19 ++-- .../context/propagation/httptextformat.py | 27 ++++-- .../opentelemetry/context/unified_context.py | 65 +++++++++++++ .../sdk/context/propagation/b3_format.py | 6 +- .../context/propagation/test_b3_format.py | 92 ++++++++++--------- 7 files changed, 154 insertions(+), 78 deletions(-) create mode 100644 opentelemetry-api/src/opentelemetry/context/unified_context.py diff --git a/opentelemetry-api/src/opentelemetry/context/__init__.py b/opentelemetry-api/src/opentelemetry/context/__init__.py index cf6c72dd8da..368b55affc9 100644 --- a/opentelemetry-api/src/opentelemetry/context/__init__.py +++ b/opentelemetry-api/src/opentelemetry/context/__init__.py @@ -11,15 +11,13 @@ # 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. - - """ The OpenTelemetry context module provides abstraction layer on top of thread-local storage and contextvars. The long term direction is to switch to contextvars provided by the Python runtime library. A global object ``Context`` is provided to access all the context related -functionalities:: +functionalities: >>> from opentelemetry.context import Context >>> Context.foo = 1 @@ -27,9 +25,8 @@ >>> Context.foo 2 -When explicit thread is used, a helper function -``Context.with_current_context`` can be used to carry the context across -threads:: +When explicit thread is used, a helper function `Context.with_current_context` +can be used to carry the context across threads: from threading import Thread from opentelemetry.context import Context @@ -62,7 +59,7 @@ def work(name): print('Main thread:', Context) -Here goes another example using thread pool:: +Here goes another example using thread pool: import time import threading @@ -97,7 +94,7 @@ def work(name): pool.join() println('Main thread: {}'.format(Context)) -Here goes a simple demo of how async could work in Python 3.7+:: +Here goes a simple demo of how async could work in Python 3.7+: import asyncio @@ -141,9 +138,9 @@ async def main(): import typing from .base_context import BaseRuntimeContext +from .unified_context import UnifiedContext -__all__ = ["Context"] - +__all__ = ["Context", "UnifiedContext"] Context = None # type: typing.Optional[BaseRuntimeContext] diff --git a/opentelemetry-api/src/opentelemetry/context/base_context.py b/opentelemetry-api/src/opentelemetry/context/base_context.py index f1e37aa91f4..7892af54f8c 100644 --- a/opentelemetry-api/src/opentelemetry/context/base_context.py +++ b/opentelemetry-api/src/opentelemetry/context/base_context.py @@ -37,7 +37,7 @@ def set(self, value: "object") -> None: raise NotImplementedError _lock = threading.Lock() - _slots = {} # type: typing.Dict[str, 'BaseRuntimeContext.Slot'] + _slots: typing.Dict[str, Slot] = {} @classmethod def clear(cls) -> None: @@ -48,9 +48,7 @@ def clear(cls) -> None: slot.clear() @classmethod - def register_slot( - cls, name: str, default: "object" = None - ) -> "BaseRuntimeContext.Slot": + def register_slot(cls, name: str, default: "object" = None) -> "Slot": """Register a context slot with an optional default value. :type name: str diff --git a/opentelemetry-api/src/opentelemetry/context/propagation/binaryformat.py b/opentelemetry-api/src/opentelemetry/context/propagation/binaryformat.py index 7f1a65882f3..dbec8f5af49 100644 --- a/opentelemetry-api/src/opentelemetry/context/propagation/binaryformat.py +++ b/opentelemetry-api/src/opentelemetry/context/propagation/binaryformat.py @@ -13,9 +13,8 @@ # limitations under the License. import abc -import typing -from opentelemetry.trace import SpanContext +from opentelemetry.context import UnifiedContext class BinaryFormat(abc.ABC): @@ -27,14 +26,14 @@ class BinaryFormat(abc.ABC): @staticmethod @abc.abstractmethod - def to_bytes(context: SpanContext) -> bytes: + def to_bytes(context: UnifiedContext) -> bytes: """Creates a byte representation of a SpanContext. to_bytes should read values from a SpanContext and return a data format to represent it, in bytes. Args: - context: the SpanContext to serialize + context: the SpanContext to serialize. Returns: A bytes representation of the SpanContext. @@ -43,15 +42,17 @@ def to_bytes(context: SpanContext) -> bytes: @staticmethod @abc.abstractmethod - def from_bytes(byte_representation: bytes) -> typing.Optional[SpanContext]: - """Return a SpanContext that was represented by bytes. + def from_bytes(context: UnifiedContext, + byte_representation: bytes) -> None: + """Populate UnifiedContext that was represented by bytes. - from_bytes should return back a SpanContext that was constructed from - the data serialized in the byte_representation passed. If it is not + from_bytes should populated UnifiedContext with data that was + serialized in the byte_representation passed. If it is not possible to read in a proper SpanContext, return None. Args: - byte_representation: the bytes to deserialize + context: The UnifiedContext to populate. + byte_representation: the bytes to deserialize. Returns: A bytes representation of the SpanContext if it is valid. diff --git a/opentelemetry-api/src/opentelemetry/context/propagation/httptextformat.py b/opentelemetry-api/src/opentelemetry/context/propagation/httptextformat.py index f3823a86d17..c8598794b61 100644 --- a/opentelemetry-api/src/opentelemetry/context/propagation/httptextformat.py +++ b/opentelemetry-api/src/opentelemetry/context/propagation/httptextformat.py @@ -15,6 +15,7 @@ import abc import typing +from opentelemetry.context import UnifiedContext from opentelemetry.trace import SpanContext Setter = typing.Callable[[object, str, str], None] @@ -35,11 +36,12 @@ class HTTPTextFormat(abc.ABC): import flask import requests from opentelemetry.context.propagation import HTTPTextFormat + from opentelemetry.trace import tracer + from opentelemetry.context import UnifiedContext PROPAGATOR = HTTPTextFormat() - def get_header_from_flask_request(request, key): return request.headers.get_all(key) @@ -48,15 +50,17 @@ def set_header_into_requests_request(request: requests.Request, request.headers[key] = value def example_route(): - span_context = PROPAGATOR.extract( - get_header_from_flask_request, + span = tracer().create_span("") + context = UnifiedContext.create(span) + PROPAGATOR.extract( + context, get_header_from_flask_request, flask.request ) request_to_downstream = requests.Request( "GET", "http://httpbin.org/get" ) PROPAGATOR.inject( - span_context, + context, set_header_into_requests_request, request_to_downstream ) @@ -70,15 +74,20 @@ def example_route(): @abc.abstractmethod def extract( - self, get_from_carrier: Getter, carrier: object - ) -> SpanContext: - """Create a SpanContext from values in the carrier. + self, + context: UnifiedContext, + get_from_carrier: Getter, + carrier: object, + ) -> None: + """Extract values from the carrier into the context. The extract function should retrieve values from the carrier - object using get_from_carrier, and use values to populate a - SpanContext value and return it. + object using get_from_carrier, and use values to populate + attributes of the UnifiedContext passed in. Args: + context: A UnifiedContext instance that will be + populated with values from the carrier. get_from_carrier: a function that can retrieve zero or more values from the carrier. In the case that the value does not exist, return an empty list. diff --git a/opentelemetry-api/src/opentelemetry/context/unified_context.py b/opentelemetry-api/src/opentelemetry/context/unified_context.py new file mode 100644 index 00000000000..7da5200d254 --- /dev/null +++ b/opentelemetry-api/src/opentelemetry/context/unified_context.py @@ -0,0 +1,65 @@ +# Copyright 2019, 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 opentelemetry.distributedcontext import DistributedContext +from opentelemetry.trace import SpanContext + + +class UnifiedContext: + """A unified context object that contains all context relevant to + telemetry. + + The UnifiedContext is a single object that composes all contexts that + are needed by the various forms of telemetry. It is intended to be an + object that can be passed as the argument to any component that needs + to read or modify content values (such as propagators). By unifying + all context in a composed data structure, it expands the flexibility + of the APIs that modify it. + + As it is designed to carry context specific to all telemetry use + cases, it's schema is explicit. Note that this is not intended to + be an object that acts as a singleton that returns different results + based on the thread or coroutine of execution. For that, see `Context`. + + + Args: + distributed: The DistributedContext for this instance. + span: The SpanContext for this instance. + """ + __slots__ = ["distributed", "span"] + + def __init__(self, distributed: DistributedContext, span: SpanContext): + self.distributed = distributed + self.span = span + + @staticmethod + def create(span: SpanContext) -> "UnifiedContext": + """Create an unpopulated UnifiedContext object. + + Example: + + from opentelemetry.trace import tracer + span = tracer.create_span("") + context = UnifiedContext.create(span) + + + Args: + parent_span: the parent SpanContext that will be the + parent of the span in the UnifiedContext. + """ + return UnifiedContext(DistributedContext(), span) + + def __repr__(self) -> str: + return "{}(distributed={}, span={})".format( + type(self).__name__, repr(self.distributed), repr(self.span)) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/context/propagation/b3_format.py b/opentelemetry-sdk/src/opentelemetry/sdk/context/propagation/b3_format.py index 72d02d60700..ae159872ce7 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/context/propagation/b3_format.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/context/propagation/b3_format.py @@ -32,7 +32,7 @@ class B3Format(HTTPTextFormat): _SAMPLE_PROPAGATE_VALUES = set(["1", "True", "true", "d"]) @classmethod - def extract(cls, get_from_carrier, carrier): + def extract(cls, context, get_from_carrier, carrier): trace_id = format_trace_id(trace.INVALID_TRACE_ID) span_id = format_span_id(trace.INVALID_SPAN_ID) sampled = 0 @@ -57,7 +57,7 @@ def extract(cls, get_from_carrier, carrier): elif len(fields) == 4: trace_id, span_id, sampled, _parent_span_id = fields else: - return trace.INVALID_SPAN_CONTEXT + return else: trace_id = ( _extract_first_element( @@ -92,7 +92,7 @@ def extract(cls, get_from_carrier, carrier): if sampled in cls._SAMPLE_PROPAGATE_VALUES or flags == "1": options |= trace.TraceOptions.RECORDED - return trace.SpanContext( + context.span = trace.SpanContext( # trace an span ids are encoded in hex, so must be converted trace_id=int(trace_id, 16), span_id=int(span_id, 16), diff --git a/opentelemetry-sdk/tests/context/propagation/test_b3_format.py b/opentelemetry-sdk/tests/context/propagation/test_b3_format.py index 42ff3410f06..613d9b6d307 100644 --- a/opentelemetry-sdk/tests/context/propagation/test_b3_format.py +++ b/opentelemetry-sdk/tests/context/propagation/test_b3_format.py @@ -17,6 +17,9 @@ import opentelemetry.sdk.context.propagation.b3_format as b3_format import opentelemetry.sdk.trace as trace +from opentelemetry.context import UnifiedContext +from opentelemetry.sdk.trace import tracer + FORMAT = b3_format.B3Format() @@ -35,6 +38,11 @@ def setUpClass(cls): trace.generate_span_id() ) + def setUp(self): + span_context = tracer.create_span("").context + self.context = UnifiedContext.create(span_context) + self.carrier = {} + def test_extract_multi_header(self): """Test the extraction of B3 headers.""" carrier = { @@ -42,16 +50,15 @@ def test_extract_multi_header(self): FORMAT.SPAN_ID_KEY: self.serialized_span_id, FORMAT.SAMPLED_KEY: "1", } - span_context = FORMAT.extract(get_as_list, carrier) - new_carrier = {} - FORMAT.inject(span_context, dict.__setitem__, new_carrier) + FORMAT.extract(self.context, get_as_list, carrier) + FORMAT.inject(self.context, dict.__setitem__, self.carrier) self.assertEqual( - new_carrier[FORMAT.TRACE_ID_KEY], self.serialized_trace_id + self.carrier[FORMAT.TRACE_ID_KEY], self.serialized_trace_id ) self.assertEqual( - new_carrier[FORMAT.SPAN_ID_KEY], self.serialized_span_id + self.carrier[FORMAT.SPAN_ID_KEY], self.serialized_span_id ) - self.assertEqual(new_carrier[FORMAT.SAMPLED_KEY], "1") + self.assertEqual(self.carrier[FORMAT.SAMPLED_KEY], "1") def test_extract_single_header(self): """Test the extraction from a single b3 header.""" @@ -60,16 +67,15 @@ def test_extract_single_header(self): self.serialized_trace_id, self.serialized_span_id ) } - span_context = FORMAT.extract(get_as_list, carrier) - new_carrier = {} - FORMAT.inject(span_context, dict.__setitem__, new_carrier) + FORMAT.extract(self.context, get_as_list, carrier) + FORMAT.inject(self.context, dict.__setitem__, self.carrier) self.assertEqual( - new_carrier[FORMAT.TRACE_ID_KEY], self.serialized_trace_id + self.carrier[FORMAT.TRACE_ID_KEY], self.serialized_trace_id ) self.assertEqual( - new_carrier[FORMAT.SPAN_ID_KEY], self.serialized_span_id + self.carrier[FORMAT.SPAN_ID_KEY], self.serialized_span_id ) - self.assertEqual(new_carrier[FORMAT.SAMPLED_KEY], "1") + self.assertEqual(self.carrier[FORMAT.SAMPLED_KEY], "1") def test_extract_header_precedence(self): """A single b3 header should take precedence over multiple @@ -84,11 +90,10 @@ def test_extract_header_precedence(self): FORMAT.SPAN_ID_KEY: self.serialized_span_id, FORMAT.SAMPLED_KEY: "1", } - span_context = FORMAT.extract(get_as_list, carrier) - new_carrier = {} - FORMAT.inject(span_context, dict.__setitem__, new_carrier) + FORMAT.extract(self.context, get_as_list, carrier) + FORMAT.inject(self.context, dict.__setitem__, self.carrier) self.assertEqual( - new_carrier[FORMAT.TRACE_ID_KEY], single_header_trace_id + self.carrier[FORMAT.TRACE_ID_KEY], single_header_trace_id ) def test_enabled_sampling(self): @@ -99,10 +104,9 @@ def test_enabled_sampling(self): FORMAT.SPAN_ID_KEY: self.serialized_span_id, FORMAT.SAMPLED_KEY: variant, } - span_context = FORMAT.extract(get_as_list, carrier) - new_carrier = {} - FORMAT.inject(span_context, dict.__setitem__, new_carrier) - self.assertEqual(new_carrier[FORMAT.SAMPLED_KEY], "1") + FORMAT.extract(self.context, get_as_list, carrier) + FORMAT.inject(self.context, dict.__setitem__, self.carrier) + self.assertEqual(self.carrier[FORMAT.SAMPLED_KEY], "1") def test_disabled_sampling(self): """Test b3 sample key variants that turn off sampling.""" @@ -112,10 +116,9 @@ def test_disabled_sampling(self): FORMAT.SPAN_ID_KEY: self.serialized_span_id, FORMAT.SAMPLED_KEY: variant, } - span_context = FORMAT.extract(get_as_list, carrier) - new_carrier = {} - FORMAT.inject(span_context, dict.__setitem__, new_carrier) - self.assertEqual(new_carrier[FORMAT.SAMPLED_KEY], "0") + FORMAT.extract(self.context, get_as_list, carrier) + FORMAT.inject(self.context, dict.__setitem__, self.carrier) + self.assertEqual(self.carrier[FORMAT.SAMPLED_KEY], "0") def test_flags(self): """x-b3-flags set to "1" should result in propagation.""" @@ -124,10 +127,9 @@ def test_flags(self): FORMAT.SPAN_ID_KEY: self.serialized_span_id, FORMAT.FLAGS_KEY: "1", } - span_context = FORMAT.extract(get_as_list, carrier) - new_carrier = {} - FORMAT.inject(span_context, dict.__setitem__, new_carrier) - self.assertEqual(new_carrier[FORMAT.SAMPLED_KEY], "1") + FORMAT.extract(self.context, get_as_list, carrier) + FORMAT.inject(self.context, dict.__setitem__, self.carrier) + self.assertEqual(self.carrier[FORMAT.SAMPLED_KEY], "1") def test_flags_and_sampling(self): """Propagate if b3 flags and sampling are set.""" @@ -136,10 +138,9 @@ def test_flags_and_sampling(self): FORMAT.SPAN_ID_KEY: self.serialized_span_id, FORMAT.FLAGS_KEY: "1", } - span_context = FORMAT.extract(get_as_list, carrier) - new_carrier = {} - FORMAT.inject(span_context, dict.__setitem__, new_carrier) - self.assertEqual(new_carrier[FORMAT.SAMPLED_KEY], "1") + FORMAT.extract(self.context, get_as_list, carrier) + FORMAT.inject(self.context, dict.__setitem__, self.carrier) + self.assertEqual(self.carrier[FORMAT.SAMPLED_KEY], "1") def test_64bit_trace_id(self): """64 bit trace ids should be padded to 128 bit trace ids.""" @@ -149,21 +150,24 @@ def test_64bit_trace_id(self): FORMAT.SPAN_ID_KEY: self.serialized_span_id, FORMAT.FLAGS_KEY: "1", } - span_context = FORMAT.extract(get_as_list, carrier) - new_carrier = {} - FORMAT.inject(span_context, dict.__setitem__, new_carrier) + FORMAT.extract(self.context, get_as_list, carrier) + FORMAT.inject(self.context, dict.__setitem__, self.carrier) self.assertEqual( - new_carrier[FORMAT.TRACE_ID_KEY], "0" * 16 + trace_id_64_bit + self.carrier[FORMAT.TRACE_ID_KEY], "0" * 16 + trace_id_64_bit ) def test_invalid_single_header(self): """If an invalid single header is passed, return an invalid SpanContext. """ + self.context.span.trace_id = api_trace.INVALID_TRACE_ID + self.context.span.span_id = api_trace.INVALID_SPAN_ID carrier = {FORMAT.SINGLE_HEADER_KEY: "0-1-2-3-4-5-6-7"} - span_context = FORMAT.extract(get_as_list, carrier) - self.assertEqual(span_context.trace_id, api_trace.INVALID_TRACE_ID) - self.assertEqual(span_context.span_id, api_trace.INVALID_SPAN_ID) + FORMAT.extract(self.context, get_as_list, carrier) + self.assertEqual( + self.context.span.trace_id, api_trace.INVALID_TRACE_ID + ) + self.assertEqual(self.context.span.span_id, api_trace.INVALID_SPAN_ID) def test_missing_trace_id(self): """If a trace id is missing, populate an invalid trace id.""" @@ -171,8 +175,10 @@ def test_missing_trace_id(self): FORMAT.SPAN_ID_KEY: self.serialized_span_id, FORMAT.FLAGS_KEY: "1", } - span_context = FORMAT.extract(get_as_list, carrier) - self.assertEqual(span_context.trace_id, api_trace.INVALID_TRACE_ID) + FORMAT.extract(self.context, get_as_list, carrier) + self.assertEqual( + self.context.span.trace_id, api_trace.INVALID_TRACE_ID + ) def test_missing_span_id(self): """If a trace id is missing, populate an invalid trace id.""" @@ -180,5 +186,5 @@ def test_missing_span_id(self): FORMAT.TRACE_ID_KEY: self.serialized_trace_id, FORMAT.FLAGS_KEY: "1", } - span_context = FORMAT.extract(get_as_list, carrier) - self.assertEqual(span_context.span_id, api_trace.INVALID_SPAN_ID) + FORMAT.extract(self.context, get_as_list, carrier) + self.assertEqual(self.context.span.span_id, api_trace.INVALID_SPAN_ID) From aab575ff11324adeb51f74f2f06da5441e1b6155 Mon Sep 17 00:00:00 2001 From: Yusuke Tsutsumi Date: Mon, 2 Sep 2019 22:06:54 -0700 Subject: [PATCH 2/7] Addressing feedback Migrating formatters outside of context. The design goal is to allow context to be it's own object agnostic of the requirements of specific telemetry. Instead, moving it under a broader propagators module. Removing UnifiedContext in lieu of directly using Context. UnifiedContext adds an unneeded layer of abstraction that would then require more operations to migrate into context. Removing binaryformat and httptextformat modules specific to SpanContext and DistributedContext. These are now handled by a single formatter. --- .../src/opentelemetry/context/__init__.py | 28 +++-- .../context/propagation/__init__.py | 4 - .../context/propagation/binaryformat.py | 61 ---------- .../opentelemetry/context/unified_context.py | 65 ---------- .../propagation/__init__.py | 18 --- .../propagation/binaryformat.py | 62 ---------- .../propagation/httptextformat.py | 114 ------------------ .../src/opentelemetry/propagator}/__init__.py | 0 .../opentelemetry/propagator/binaryformat.py | 0 .../httptextformat.py | 10 +- .../sdk/distributedcontext/__init__.py | 4 +- .../propagation => propagator}/b3_format.py | 18 ++- .../src/opentelemetry/sdk/trace/__init__.py | 4 +- .../tests/propagator/__init__.py | 0 .../test_b3_format.py | 23 ++-- 15 files changed, 56 insertions(+), 355 deletions(-) delete mode 100644 opentelemetry-api/src/opentelemetry/context/propagation/__init__.py delete mode 100644 opentelemetry-api/src/opentelemetry/context/propagation/binaryformat.py delete mode 100644 opentelemetry-api/src/opentelemetry/context/unified_context.py delete mode 100644 opentelemetry-api/src/opentelemetry/distributedcontext/propagation/__init__.py delete mode 100644 opentelemetry-api/src/opentelemetry/distributedcontext/propagation/binaryformat.py delete mode 100644 opentelemetry-api/src/opentelemetry/distributedcontext/propagation/httptextformat.py rename {opentelemetry-sdk/src/opentelemetry/sdk/context => opentelemetry-api/src/opentelemetry/propagator}/__init__.py (100%) rename opentelemetry-sdk/src/opentelemetry/sdk/context/propagation/__init__.py => opentelemetry-api/src/opentelemetry/propagator/binaryformat.py (100%) rename opentelemetry-api/src/opentelemetry/{context/propagation => propagator}/httptextformat.py (95%) rename opentelemetry-sdk/src/opentelemetry/sdk/{context/propagation => propagator}/b3_format.py (85%) create mode 100644 opentelemetry-sdk/tests/propagator/__init__.py rename opentelemetry-sdk/tests/{context/propagation => propagator}/test_b3_format.py (90%) diff --git a/opentelemetry-api/src/opentelemetry/context/__init__.py b/opentelemetry-api/src/opentelemetry/context/__init__.py index 368b55affc9..6fa5e72e345 100644 --- a/opentelemetry-api/src/opentelemetry/context/__init__.py +++ b/opentelemetry-api/src/opentelemetry/context/__init__.py @@ -135,20 +135,26 @@ async def main(): asyncio.run(main()) """ -import typing - from .base_context import BaseRuntimeContext -from .unified_context import UnifiedContext -__all__ = ["Context", "UnifiedContext"] +__all__ = ["Context"] + + +def create_context() -> BaseRuntimeContext: + """Create a new context object. + + Return a new RuntimeContext, instantiating the variant that + is the most appropriate for the version of Python being used. + """ + + try: + from .async_context import AsyncRuntimeContext -Context = None # type: typing.Optional[BaseRuntimeContext] + return AsyncRuntimeContext() + except ImportError: + from .thread_local_context import ThreadLocalRuntimeContext -try: - from .async_context import AsyncRuntimeContext + return ThreadLocalRuntimeContext() - Context = AsyncRuntimeContext() -except ImportError: - from .thread_local_context import ThreadLocalRuntimeContext - Context = ThreadLocalRuntimeContext() +Context = create_context() diff --git a/opentelemetry-api/src/opentelemetry/context/propagation/__init__.py b/opentelemetry-api/src/opentelemetry/context/propagation/__init__.py deleted file mode 100644 index b964c2a968d..00000000000 --- a/opentelemetry-api/src/opentelemetry/context/propagation/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .binaryformat import BinaryFormat -from .httptextformat import HTTPTextFormat - -__all__ = ["BinaryFormat", "HTTPTextFormat"] diff --git a/opentelemetry-api/src/opentelemetry/context/propagation/binaryformat.py b/opentelemetry-api/src/opentelemetry/context/propagation/binaryformat.py deleted file mode 100644 index dbec8f5af49..00000000000 --- a/opentelemetry-api/src/opentelemetry/context/propagation/binaryformat.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright 2019, 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. - -import abc - -from opentelemetry.context import UnifiedContext - - -class BinaryFormat(abc.ABC): - """API for serialization of span context into binary formats. - - This class provides an interface that enables converting span contexts - to and from a binary format. - """ - - @staticmethod - @abc.abstractmethod - def to_bytes(context: UnifiedContext) -> bytes: - """Creates a byte representation of a SpanContext. - - to_bytes should read values from a SpanContext and return a data - format to represent it, in bytes. - - Args: - context: the SpanContext to serialize. - - Returns: - A bytes representation of the SpanContext. - - """ - - @staticmethod - @abc.abstractmethod - def from_bytes(context: UnifiedContext, - byte_representation: bytes) -> None: - """Populate UnifiedContext that was represented by bytes. - - from_bytes should populated UnifiedContext with data that was - serialized in the byte_representation passed. If it is not - possible to read in a proper SpanContext, return None. - - Args: - context: The UnifiedContext to populate. - byte_representation: the bytes to deserialize. - - Returns: - A bytes representation of the SpanContext if it is valid. - Otherwise return None. - - """ diff --git a/opentelemetry-api/src/opentelemetry/context/unified_context.py b/opentelemetry-api/src/opentelemetry/context/unified_context.py deleted file mode 100644 index 7da5200d254..00000000000 --- a/opentelemetry-api/src/opentelemetry/context/unified_context.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright 2019, 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 opentelemetry.distributedcontext import DistributedContext -from opentelemetry.trace import SpanContext - - -class UnifiedContext: - """A unified context object that contains all context relevant to - telemetry. - - The UnifiedContext is a single object that composes all contexts that - are needed by the various forms of telemetry. It is intended to be an - object that can be passed as the argument to any component that needs - to read or modify content values (such as propagators). By unifying - all context in a composed data structure, it expands the flexibility - of the APIs that modify it. - - As it is designed to carry context specific to all telemetry use - cases, it's schema is explicit. Note that this is not intended to - be an object that acts as a singleton that returns different results - based on the thread or coroutine of execution. For that, see `Context`. - - - Args: - distributed: The DistributedContext for this instance. - span: The SpanContext for this instance. - """ - __slots__ = ["distributed", "span"] - - def __init__(self, distributed: DistributedContext, span: SpanContext): - self.distributed = distributed - self.span = span - - @staticmethod - def create(span: SpanContext) -> "UnifiedContext": - """Create an unpopulated UnifiedContext object. - - Example: - - from opentelemetry.trace import tracer - span = tracer.create_span("") - context = UnifiedContext.create(span) - - - Args: - parent_span: the parent SpanContext that will be the - parent of the span in the UnifiedContext. - """ - return UnifiedContext(DistributedContext(), span) - - def __repr__(self) -> str: - return "{}(distributed={}, span={})".format( - type(self).__name__, repr(self.distributed), repr(self.span)) diff --git a/opentelemetry-api/src/opentelemetry/distributedcontext/propagation/__init__.py b/opentelemetry-api/src/opentelemetry/distributedcontext/propagation/__init__.py deleted file mode 100644 index c8706281ad7..00000000000 --- a/opentelemetry-api/src/opentelemetry/distributedcontext/propagation/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright 2019, 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 .binaryformat import BinaryFormat -from .httptextformat import HTTPTextFormat - -__all__ = ["BinaryFormat", "HTTPTextFormat"] diff --git a/opentelemetry-api/src/opentelemetry/distributedcontext/propagation/binaryformat.py b/opentelemetry-api/src/opentelemetry/distributedcontext/propagation/binaryformat.py deleted file mode 100644 index 15f8cfdf63d..00000000000 --- a/opentelemetry-api/src/opentelemetry/distributedcontext/propagation/binaryformat.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright 2019, 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. - -import abc -import typing - -from opentelemetry.distributedcontext import DistributedContext - - -class BinaryFormat(abc.ABC): - """API for serialization of span context into binary formats. - - This class provides an interface that enables converting span contexts - to and from a binary format. - """ - - @staticmethod - @abc.abstractmethod - def to_bytes(context: DistributedContext) -> bytes: - """Creates a byte representation of a DistributedContext. - - to_bytes should read values from a DistributedContext and return a data - format to represent it, in bytes. - - Args: - context: the DistributedContext to serialize - - Returns: - A bytes representation of the DistributedContext. - - """ - - @staticmethod - @abc.abstractmethod - def from_bytes( - byte_representation: bytes - ) -> typing.Optional[DistributedContext]: - """Return a DistributedContext that was represented by bytes. - - from_bytes should return back a DistributedContext that was constructed - from the data serialized in the byte_representation passed. If it is - not possible to read in a proper DistributedContext, return None. - - Args: - byte_representation: the bytes to deserialize - - Returns: - A bytes representation of the DistributedContext if it is valid. - Otherwise return None. - - """ diff --git a/opentelemetry-api/src/opentelemetry/distributedcontext/propagation/httptextformat.py b/opentelemetry-api/src/opentelemetry/distributedcontext/propagation/httptextformat.py deleted file mode 100644 index 3e2c186283c..00000000000 --- a/opentelemetry-api/src/opentelemetry/distributedcontext/propagation/httptextformat.py +++ /dev/null @@ -1,114 +0,0 @@ -# Copyright 2019, 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. - -import abc -import typing - -from opentelemetry.distributedcontext import DistributedContext - -Setter = typing.Callable[[object, str, str], None] -Getter = typing.Callable[[object, str], typing.List[str]] - - -class HTTPTextFormat(abc.ABC): - """API for propagation of span context via headers. - - This class provides an interface that enables extracting and injecting - span context into headers of HTTP requests. HTTP frameworks and clients - can integrate with HTTPTextFormat by providing the object containing the - headers, and a getter and setter function for the extraction and - injection of values, respectively. - - Example:: - - import flask - import requests - from opentelemetry.context.propagation import HTTPTextFormat - - PROPAGATOR = HTTPTextFormat() - - def get_header_from_flask_request(request, key): - return request.headers.get_all(key) - - def set_header_into_requests_request(request: requests.Request, - key: str, value: str): - request.headers[key] = value - - def example_route(): - distributed_context = PROPAGATOR.extract( - get_header_from_flask_request, - flask.request - ) - request_to_downstream = requests.Request( - "GET", "http://httpbin.org/get" - ) - PROPAGATOR.inject( - distributed_context, - set_header_into_requests_request, - request_to_downstream - ) - session = requests.Session() - session.send(request_to_downstream.prepare()) - - - .. _Propagation API Specification: - https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/api-propagators.md - """ - - @abc.abstractmethod - def extract( - self, get_from_carrier: Getter, carrier: object - ) -> DistributedContext: - """Create a DistributedContext from values in the carrier. - - The extract function should retrieve values from the carrier - object using get_from_carrier, and use values to populate a - DistributedContext value and return it. - - Args: - get_from_carrier: a function that can retrieve zero - or more values from the carrier. In the case that - the value does not exist, return an empty list. - carrier: and object which contains values that are - used to construct a DistributedContext. This object - must be paired with an appropriate get_from_carrier - which understands how to extract a value from it. - Returns: - A DistributedContext with configuration found in the carrier. - - """ - - @abc.abstractmethod - def inject( - self, - context: DistributedContext, - set_in_carrier: Setter, - carrier: object, - ) -> None: - """Inject values from a DistributedContext into a carrier. - - inject enables the propagation of values into HTTP clients or - other objects which perform an HTTP request. Implementations - should use the set_in_carrier method to set values on the - carrier. - - Args: - context: The DistributedContext to read values from. - set_in_carrier: A setter function that can set values - on the carrier. - carrier: An object that a place to define HTTP headers. - Should be paired with set_in_carrier, which should - know how to set header values on the carrier. - - """ diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/context/__init__.py b/opentelemetry-api/src/opentelemetry/propagator/__init__.py similarity index 100% rename from opentelemetry-sdk/src/opentelemetry/sdk/context/__init__.py rename to opentelemetry-api/src/opentelemetry/propagator/__init__.py diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/context/propagation/__init__.py b/opentelemetry-api/src/opentelemetry/propagator/binaryformat.py similarity index 100% rename from opentelemetry-sdk/src/opentelemetry/sdk/context/propagation/__init__.py rename to opentelemetry-api/src/opentelemetry/propagator/binaryformat.py diff --git a/opentelemetry-api/src/opentelemetry/context/propagation/httptextformat.py b/opentelemetry-api/src/opentelemetry/propagator/httptextformat.py similarity index 95% rename from opentelemetry-api/src/opentelemetry/context/propagation/httptextformat.py rename to opentelemetry-api/src/opentelemetry/propagator/httptextformat.py index c8598794b61..429bf980ebd 100644 --- a/opentelemetry-api/src/opentelemetry/context/propagation/httptextformat.py +++ b/opentelemetry-api/src/opentelemetry/propagator/httptextformat.py @@ -15,8 +15,7 @@ import abc import typing -from opentelemetry.context import UnifiedContext -from opentelemetry.trace import SpanContext +from opentelemetry.context import BaseRuntimeContext Setter = typing.Callable[[object, str, str], None] Getter = typing.Callable[[object, str], typing.List[str]] @@ -75,7 +74,7 @@ def example_route(): @abc.abstractmethod def extract( self, - context: UnifiedContext, + context: BaseRuntimeContext, get_from_carrier: Getter, carrier: object, ) -> None: @@ -102,7 +101,10 @@ def extract( @abc.abstractmethod def inject( - self, context: SpanContext, set_in_carrier: Setter, carrier: object + self, + context: BaseRuntimeContext, + set_in_carrier: Setter, + carrier: object, ) -> None: """Inject values from a SpanContext into a carrier. diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/distributedcontext/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/distributedcontext/__init__.py index a20cbf89635..24d8135db4f 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/distributedcontext/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/distributedcontext/__init__.py @@ -26,11 +26,13 @@ class DistributedContextManager(dctx_api.DistributedContextManager): name: The name of the context manager """ + CONTEXT_SLOT_NAME = "DistributedContext" + def __init__(self, name: str = "") -> None: if name: slot_name = "DistributedContext.{}".format(name) else: - slot_name = "DistributedContext" + slot_name = self.CONTEXT_SLOT_NAME self._current_context = Context.register_slot(slot_name) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/context/propagation/b3_format.py b/opentelemetry-sdk/src/opentelemetry/sdk/propagator/b3_format.py similarity index 85% rename from opentelemetry-sdk/src/opentelemetry/sdk/context/propagation/b3_format.py rename to opentelemetry-sdk/src/opentelemetry/sdk/propagator/b3_format.py index ae159872ce7..5d2ce7afcef 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/context/propagation/b3_format.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/propagator/b3_format.py @@ -15,7 +15,8 @@ import typing import opentelemetry.trace as trace -from opentelemetry.context.propagation.httptextformat import HTTPTextFormat +from opentelemetry.propagator.httptextformat import HTTPTextFormat +from opentelemetry.sdk.trace import Tracer class B3Format(HTTPTextFormat): @@ -92,7 +93,11 @@ def extract(cls, context, get_from_carrier, carrier): if sampled in cls._SAMPLE_PROPAGATE_VALUES or flags == "1": options |= trace.TraceOptions.RECORDED - context.span = trace.SpanContext( + # TODO: there is no standard way to retrieve + # the SpanContext or DistributedContext from a + # context object declared in opentelemetry-api. + # this should be more standard. + context[Tracer.CONTEXT_SLOT_NAME] = trace.SpanContext( # trace an span ids are encoded in hex, so must be converted trace_id=int(trace_id, 16), span_id=int(span_id, 16), @@ -102,12 +107,15 @@ def extract(cls, context, get_from_carrier, carrier): @classmethod def inject(cls, context, set_in_carrier, carrier): - sampled = (trace.TraceOptions.RECORDED & context.trace_options) != 0 + span_context = context[Tracer.CONTEXT_SLOT_NAME] + sampled = ( + trace.TraceOptions.RECORDED & span_context.trace_options + ) != 0 set_in_carrier( - carrier, cls.TRACE_ID_KEY, format_trace_id(context.trace_id) + carrier, cls.TRACE_ID_KEY, format_trace_id(span_context.trace_id) ) set_in_carrier( - carrier, cls.SPAN_ID_KEY, format_span_id(context.span_id) + carrier, cls.SPAN_ID_KEY, format_span_id(span_context.span_id) ) set_in_carrier(carrier, cls.SAMPLED_KEY, "1" if sampled else "0") diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py index 72c5c303469..8d186c56723 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py @@ -281,8 +281,10 @@ class Tracer(trace_api.Tracer): name: The name of the tracer. """ + CONTEXT_SLOT_NAME = "current_span" + def __init__(self, name: str = "") -> None: - slot_name = "current_span" + slot_name = self.CONTEXT_SLOT_NAME if name: slot_name = "{}.current_span".format(name) self._current_span_slot = Context.register_slot(slot_name) diff --git a/opentelemetry-sdk/tests/propagator/__init__.py b/opentelemetry-sdk/tests/propagator/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/opentelemetry-sdk/tests/context/propagation/test_b3_format.py b/opentelemetry-sdk/tests/propagator/test_b3_format.py similarity index 90% rename from opentelemetry-sdk/tests/context/propagation/test_b3_format.py rename to opentelemetry-sdk/tests/propagator/test_b3_format.py index 613d9b6d307..d65af264ede 100644 --- a/opentelemetry-sdk/tests/context/propagation/test_b3_format.py +++ b/opentelemetry-sdk/tests/propagator/test_b3_format.py @@ -14,10 +14,10 @@ import unittest import opentelemetry.trace as api_trace -import opentelemetry.sdk.context.propagation.b3_format as b3_format +import opentelemetry.context as context +import opentelemetry.sdk.propagator.b3_format as b3_format import opentelemetry.sdk.trace as trace -from opentelemetry.context import UnifiedContext from opentelemetry.sdk.trace import tracer FORMAT = b3_format.B3Format() @@ -40,9 +40,14 @@ def setUpClass(cls): def setUp(self): span_context = tracer.create_span("").context - self.context = UnifiedContext.create(span_context) + self.context = context.create_context() + self.context[tracer.CONTEXT_SLOT_NAME] = span_context self.carrier = {} + @property + def _span_context(self): + return self.context[tracer.CONTEXT_SLOT_NAME] + def test_extract_multi_header(self): """Test the extraction of B3 headers.""" carrier = { @@ -160,14 +165,14 @@ def test_invalid_single_header(self): """If an invalid single header is passed, return an invalid SpanContext. """ - self.context.span.trace_id = api_trace.INVALID_TRACE_ID - self.context.span.span_id = api_trace.INVALID_SPAN_ID + self._span_context.trace_id = api_trace.INVALID_TRACE_ID + self._span_context.span_id = api_trace.INVALID_TRACE_ID carrier = {FORMAT.SINGLE_HEADER_KEY: "0-1-2-3-4-5-6-7"} FORMAT.extract(self.context, get_as_list, carrier) self.assertEqual( - self.context.span.trace_id, api_trace.INVALID_TRACE_ID + self._span_context.trace_id, api_trace.INVALID_TRACE_ID ) - self.assertEqual(self.context.span.span_id, api_trace.INVALID_SPAN_ID) + self.assertEqual(self._span_context.span_id, api_trace.INVALID_SPAN_ID) def test_missing_trace_id(self): """If a trace id is missing, populate an invalid trace id.""" @@ -177,7 +182,7 @@ def test_missing_trace_id(self): } FORMAT.extract(self.context, get_as_list, carrier) self.assertEqual( - self.context.span.trace_id, api_trace.INVALID_TRACE_ID + self._span_context.trace_id, api_trace.INVALID_TRACE_ID ) def test_missing_span_id(self): @@ -187,4 +192,4 @@ def test_missing_span_id(self): FORMAT.FLAGS_KEY: "1", } FORMAT.extract(self.context, get_as_list, carrier) - self.assertEqual(self.context.span.span_id, api_trace.INVALID_SPAN_ID) + self.assertEqual(self._span_context.span_id, api_trace.INVALID_SPAN_ID) From 37f57b6d6287b310f80041b196db0b00c78295a6 Mon Sep 17 00:00:00 2001 From: Yusuke Tsutsumi Date: Mon, 2 Sep 2019 22:24:23 -0700 Subject: [PATCH 3/7] re-adding missing BinaryFormat API. --- .../opentelemetry/propagator/binaryformat.py | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/opentelemetry-api/src/opentelemetry/propagator/binaryformat.py b/opentelemetry-api/src/opentelemetry/propagator/binaryformat.py index e69de29bb2d..07de0212c29 100644 --- a/opentelemetry-api/src/opentelemetry/propagator/binaryformat.py +++ b/opentelemetry-api/src/opentelemetry/propagator/binaryformat.py @@ -0,0 +1,57 @@ +# Copyright 2019, 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. + +import abc + +from opentelemetry.context import BaseRuntimeContext + + +class BinaryFormat(abc.ABC): + """API for serialization of span context into binary formats. + + This class provides an interface that enables converting span contexts + to and from a binary format. + """ + + @staticmethod + @abc.abstractmethod + def to_bytes(context: BaseRuntimeContext) -> bytes: + """Creates a byte representation of a Context. + + to_bytes should read values from a Context and return a data + format to represent it, in bytes. + + Args: + context: the Context to serialize. + + Returns: + A bytes representation of the DistributedContext. + + """ + + @staticmethod + @abc.abstractmethod + def from_bytes( + context: BaseRuntimeContext, byte_representation: bytes + ) -> None: + """Populate context with context data that existed in the byte representation. + + from_bytes should add values into the context from the data serialized in the + byte_representation passed. If it is not possible to read in a proper + DistributedContext, return None. + + Args: + context: the Context to add values into. + byte_representation: the bytes to deserialize. + """ From 1f37b2a20d00df179ab4b55eba475ed1ef9153ba Mon Sep 17 00:00:00 2001 From: Yusuke Tsutsumi Date: Tue, 3 Sep 2019 08:35:12 -0700 Subject: [PATCH 4/7] Removing mention of UnifiedContext Removing the last few mentions of UnifiedContext Fixing rst syntax of the context module. --- .../src/opentelemetry/context/__init__.py | 4 ++-- .../propagator/httptextformat.py | 20 +++++++++++-------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/context/__init__.py b/opentelemetry-api/src/opentelemetry/context/__init__.py index 6fa5e72e345..81f9032e364 100644 --- a/opentelemetry-api/src/opentelemetry/context/__init__.py +++ b/opentelemetry-api/src/opentelemetry/context/__init__.py @@ -17,7 +17,7 @@ contextvars provided by the Python runtime library. A global object ``Context`` is provided to access all the context related -functionalities: +functionalities:: >>> from opentelemetry.context import Context >>> Context.foo = 1 @@ -94,7 +94,7 @@ def work(name): pool.join() println('Main thread: {}'.format(Context)) -Here goes a simple demo of how async could work in Python 3.7+: +Here goes a simple demo of how async could work in Python 3.7+:: import asyncio diff --git a/opentelemetry-api/src/opentelemetry/propagator/httptextformat.py b/opentelemetry-api/src/opentelemetry/propagator/httptextformat.py index 429bf980ebd..22b9e678b3d 100644 --- a/opentelemetry-api/src/opentelemetry/propagator/httptextformat.py +++ b/opentelemetry-api/src/opentelemetry/propagator/httptextformat.py @@ -34,9 +34,9 @@ class HTTPTextFormat(abc.ABC): import flask import requests - from opentelemetry.context.propagation import HTTPTextFormat + from opentelemetry.context import Context + from opentelemetry.propagator.httptextformat import HTTPTextFormat from opentelemetry.trace import tracer - from opentelemetry.context import UnifiedContext PROPAGATOR = HTTPTextFormat() @@ -50,16 +50,15 @@ def set_header_into_requests_request(request: requests.Request, def example_route(): span = tracer().create_span("") - context = UnifiedContext.create(span) PROPAGATOR.extract( - context, get_header_from_flask_request, + Context, get_header_from_flask_request, flask.request ) request_to_downstream = requests.Request( "GET", "http://httpbin.org/get" ) PROPAGATOR.inject( - context, + Context, set_header_into_requests_request, request_to_downstream ) @@ -82,10 +81,14 @@ def extract( The extract function should retrieve values from the carrier object using get_from_carrier, and use values to populate - attributes of the UnifiedContext passed in. + attributes of the context passed in. + + As the context instance is a container of state, the format + would modify the specific context that the context + contains (e.g. SpanContext or DistributedContext). Args: - context: A UnifiedContext instance that will be + context: A Context instance that will be populated with values from the carrier. get_from_carrier: a function that can retrieve zero or more values from the carrier. In the case that @@ -114,7 +117,8 @@ def inject( carrier. Args: - context: The SpanContext to read values from. + context: The context to retrieve specific contexts from, + whose values would be injecting into the carrier. set_in_carrier: A setter function that can set values on the carrier. carrier: An object that a place to define HTTP headers. From dc063a2bccbf2fb580806bc548604e0c3365ab01 Mon Sep 17 00:00:00 2001 From: Yusuke Tsutsumi Date: Thu, 5 Sep 2019 09:17:00 -0700 Subject: [PATCH 5/7] Fixing sphinx warnings --- opentelemetry-api/src/opentelemetry/context/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/context/__init__.py b/opentelemetry-api/src/opentelemetry/context/__init__.py index 81f9032e364..e71aba8c2ad 100644 --- a/opentelemetry-api/src/opentelemetry/context/__init__.py +++ b/opentelemetry-api/src/opentelemetry/context/__init__.py @@ -25,8 +25,8 @@ >>> Context.foo 2 -When explicit thread is used, a helper function `Context.with_current_context` -can be used to carry the context across threads: +When explicit thread is used, a helper function `BaseRuntimeContext.with_current_context` +can be used to carry the context across threads:: from threading import Thread from opentelemetry.context import Context @@ -59,7 +59,7 @@ def work(name): print('Main thread:', Context) -Here goes another example using thread pool: +Here goes another example using thread pool:: import time import threading From c031df340f6d722f05a65bbbf43ace82eb991af0 Mon Sep 17 00:00:00 2001 From: Yusuke Tsutsumi Date: Thu, 5 Sep 2019 10:38:13 -0700 Subject: [PATCH 6/7] fixing type annotations for sphinx --- opentelemetry-api/src/opentelemetry/context/base_context.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/opentelemetry-api/src/opentelemetry/context/base_context.py b/opentelemetry-api/src/opentelemetry/context/base_context.py index 7892af54f8c..a3040dc216b 100644 --- a/opentelemetry-api/src/opentelemetry/context/base_context.py +++ b/opentelemetry-api/src/opentelemetry/context/base_context.py @@ -48,7 +48,9 @@ def clear(cls) -> None: slot.clear() @classmethod - def register_slot(cls, name: str, default: "object" = None) -> "Slot": + def register_slot( + cls, name: str, default: "object" = None + ) -> "BaseRuntimeContext.Slot": """Register a context slot with an optional default value. :type name: str From 3d8dbde098ff2123aa51ae3a8d14e5a4b6338683 Mon Sep 17 00:00:00 2001 From: Yusuke Tsutsumi Date: Thu, 5 Sep 2019 10:50:56 -0700 Subject: [PATCH 7/7] reverting type changes on class attributes Python3.4 and 3.5 do not support types on class attributes. --- opentelemetry-api/src/opentelemetry/context/base_context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry-api/src/opentelemetry/context/base_context.py b/opentelemetry-api/src/opentelemetry/context/base_context.py index a3040dc216b..f1e37aa91f4 100644 --- a/opentelemetry-api/src/opentelemetry/context/base_context.py +++ b/opentelemetry-api/src/opentelemetry/context/base_context.py @@ -37,7 +37,7 @@ def set(self, value: "object") -> None: raise NotImplementedError _lock = threading.Lock() - _slots: typing.Dict[str, Slot] = {} + _slots = {} # type: typing.Dict[str, 'BaseRuntimeContext.Slot'] @classmethod def clear(cls) -> None: