diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1b962de528..41801fcdfb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,7 +6,7 @@ on: - 'release/*' pull_request: env: - CORE_REPO_SHA: 73601dfc0c8ab622fb8a2bfd4c0b1ea6c3f91fa3 + CORE_REPO_SHA: d7acfd8135eba4cfdb88831399089aa1d43d49d1 jobs: build: @@ -23,7 +23,7 @@ jobs: fail-fast: false # ensures the entire test matrix is run, even if one permutation fails matrix: python-version: [ py35, py36, py37, py38, pypy3 ] - package: ["instrumentation", "exporter", "sdkextension"] + package: ["instrumentation", "exporter", "sdkextension", "propagator"] os: [ ubuntu-latest ] include: # py35-instrumentation segfaults on 18.04 so we instead run on 20.04 diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a6cb86c15..1a3259dbd4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased](https://github.com/open-telemetry/opentelemetry-python-contrib/compare/v0.17b0...HEAD) -### Changed +### Added +- `opentelemetry-propagator-ot-trace` Add OT Trace Propagator + ([#302](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/302)) + +### Removed - Remove `component` span attribute in instrumentations. `opentelemetry-instrumentation-aiopg`, `opentelemetry-instrumentation-dbapi` Remove unused `database_type` parameter from `trace_integration` function. ([#301](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/301)) diff --git a/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/__init__.py b/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/__init__.py index 089883da40..57953d57e2 100644 --- a/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/__init__.py +++ b/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/__init__.py @@ -34,7 +34,8 @@ .. code:: python - from opentelemetry import propagators, trace + from opentelemetry.propagate import set_global_textmap + from opentelemetry import trace from opentelemetry.exporter.datadog import DatadogExportSpanProcessor, DatadogSpanExporter from opentelemetry.exporter.datadog.propagator import DatadogFormat from opentelemetry.sdk.trace import TracerProvider @@ -50,7 +51,7 @@ trace.get_tracer_provider().add_span_processor(span_processor) # Optional: use Datadog format for propagation in distributed traces - propagators.set_global_textmap(DatadogFormat()) + set_global_textmap(DatadogFormat()) with tracer.start_as_current_span("foo"): print("Hello world!") diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/__init__.py b/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/__init__.py index 1ff76e96da..24dd360533 100644 --- a/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/__init__.py @@ -62,7 +62,6 @@ def strip_query_params(url: yarl.URL) -> str: --- """ -import socket import types import typing @@ -70,13 +69,14 @@ def strip_query_params(url: yarl.URL) -> str: import wrapt from opentelemetry import context as context_api -from opentelemetry import propagators, trace +from opentelemetry import trace from opentelemetry.instrumentation.aiohttp_client.version import __version__ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor from opentelemetry.instrumentation.utils import ( http_status_to_status_code, unwrap, ) +from opentelemetry.propagate import inject from opentelemetry.trace import SpanKind, TracerProvider, get_tracer from opentelemetry.trace.status import Status, StatusCode @@ -181,7 +181,7 @@ async def on_request_start( trace.set_span_in_context(trace_config_ctx.span) ) - propagators.inject(type(params.headers).__setitem__, params.headers) + inject(type(params.headers).__setitem__, params.headers) async def on_request_end( unused_session: aiohttp.ClientSession, diff --git a/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py b/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py index bf5e0843ff..e0bcf00d94 100644 --- a/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py @@ -25,9 +25,10 @@ from asgiref.compatibility import guarantee_single_callable -from opentelemetry import context, propagators, trace +from opentelemetry import context, trace from opentelemetry.instrumentation.asgi.version import __version__ # noqa from opentelemetry.instrumentation.utils import http_status_to_status_code +from opentelemetry.propagate import extract from opentelemetry.trace.propagation.textmap import DictGetter from opentelemetry.trace.status import Status, StatusCode @@ -185,7 +186,7 @@ async def __call__(self, scope, receive, send): if self.excluded_urls and self.excluded_urls.url_disabled(url): return await self.app(scope, receive, send) - token = context.attach(propagators.extract(carrier_getter, scope)) + token = context.attach(extract(carrier_getter, scope)) span_name, additional_attributes = self.span_details_callback(scope) try: diff --git a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/__init__.py b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/__init__.py index 3ba8af1c02..5adab4e3c8 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/__init__.py @@ -49,15 +49,14 @@ import logging from botocore.client import BaseClient -from botocore.exceptions import ClientError, ParamValidationError -from wrapt import ObjectProxy, wrap_function_wrapper +from botocore.exceptions import ClientError +from wrapt import wrap_function_wrapper from opentelemetry import context as context_api -from opentelemetry import propagators from opentelemetry.instrumentation.botocore.version import __version__ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor from opentelemetry.instrumentation.utils import unwrap -from opentelemetry.sdk.trace import Resource +from opentelemetry.propagate import inject from opentelemetry.trace import SpanKind, get_tracer logger = logging.getLogger(__name__) @@ -67,7 +66,7 @@ def _patched_endpoint_prepare_request(wrapped, instance, args, kwargs): request = args[0] headers = request.headers - propagators.inject(type(headers).__setitem__, headers) + inject(type(headers).__setitem__, headers) return wrapped(*args, **kwargs) diff --git a/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_instrumentation.py b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_instrumentation.py index a0c1c4ac36..8781a9c3b3 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_instrumentation.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_instrumentation.py @@ -28,10 +28,10 @@ mock_xray, ) -from opentelemetry import propagators from opentelemetry import trace as trace_api from opentelemetry.context import attach, detach, set_value from opentelemetry.instrumentation.botocore import BotocoreInstrumentor +from opentelemetry.propagate import get_global_textmap, set_global_textmap from opentelemetry.test.mock_textmap import MockTextMapPropagator from opentelemetry.test.test_base import TestBase @@ -381,14 +381,14 @@ def test_sts_client(self): @mock_ec2 def test_propagator_injects_into_request(self): headers = {} - previous_propagator = propagators.get_global_textmap() + previous_propagator = get_global_textmap() def check_headers(**kwargs): nonlocal headers headers = kwargs["request"].headers try: - propagators.set_global_textmap(MockTextMapPropagator()) + set_global_textmap(MockTextMapPropagator()) ec2 = self.session.create_client("ec2", region_name="us-west-2") ec2.meta.events.register_first( @@ -424,7 +424,7 @@ def check_headers(**kwargs): ) finally: - propagators.set_global_textmap(previous_propagator) + set_global_textmap(previous_propagator) @mock_xray def test_suppress_instrumentation_xray_client(self): diff --git a/instrumentation/opentelemetry-instrumentation-celery/src/opentelemetry/instrumentation/celery/__init__.py b/instrumentation/opentelemetry-instrumentation-celery/src/opentelemetry/instrumentation/celery/__init__.py index 495cbe96a1..d542950af3 100644 --- a/instrumentation/opentelemetry-instrumentation-celery/src/opentelemetry/instrumentation/celery/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-celery/src/opentelemetry/instrumentation/celery/__init__.py @@ -52,15 +52,15 @@ def add(x, y): """ import logging -import signal from collections.abc import Iterable from celery import signals # pylint: disable=no-name-in-module -from opentelemetry import propagators, trace +from opentelemetry import trace from opentelemetry.instrumentation.celery import utils from opentelemetry.instrumentation.celery.version import __version__ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor +from opentelemetry.propagate import extract, inject from opentelemetry.trace.propagation.textmap import DictGetter from opentelemetry.trace.status import Status, StatusCode @@ -128,7 +128,7 @@ def _trace_prerun(self, *args, **kwargs): return request = task.request - tracectx = propagators.extract(carrier_getter, request) or None + tracectx = extract(carrier_getter, request) or None logger.debug("prerun signal start task_id=%s", task_id) @@ -192,7 +192,7 @@ def _trace_before_publish(self, *args, **kwargs): headers = kwargs.get("headers") if headers: - propagators.inject(type(headers).__setitem__, headers) + inject(type(headers).__setitem__, headers) @staticmethod def _trace_after_publish(*args, **kwargs): diff --git a/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware.py b/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware.py index cf9ffcc8a8..86e408d064 100644 --- a/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware.py +++ b/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware.py @@ -23,7 +23,7 @@ carrier_getter, collect_request_attributes, ) -from opentelemetry.propagators import extract +from opentelemetry.propagate import extract from opentelemetry.trace import SpanKind, get_tracer from opentelemetry.util.http import get_excluded_urls, get_traced_request_attrs diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/src/opentelemetry/instrumentation/elasticsearch/__init__.py b/instrumentation/opentelemetry-instrumentation-elasticsearch/src/opentelemetry/instrumentation/elasticsearch/__init__.py index 848ec135a8..69bd060169 100644 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/src/opentelemetry/instrumentation/elasticsearch/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-elasticsearch/src/opentelemetry/instrumentation/elasticsearch/__init__.py @@ -46,17 +46,13 @@ ElasticsearchInstrumentor("my-custom-prefix").instrument() """ -import functools -import types from logging import getLogger from os import environ import elasticsearch import elasticsearch.exceptions -from wrapt import ObjectProxy from wrapt import wrap_function_wrapper as _wrap -from opentelemetry import context, propagators, trace from opentelemetry.instrumentation.elasticsearch.version import __version__ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor from opentelemetry.instrumentation.utils import unwrap diff --git a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py index 70d938ac8f..5d7945e4af 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py @@ -49,13 +49,14 @@ def on_get(self, req, resp): import falcon import opentelemetry.instrumentation.wsgi as otel_wsgi -from opentelemetry import context, propagators, trace +from opentelemetry import context, trace from opentelemetry.instrumentation.falcon.version import __version__ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor from opentelemetry.instrumentation.utils import ( extract_attributes_from_object, http_status_to_status_code, ) +from opentelemetry.propagate import extract from opentelemetry.trace.status import Status from opentelemetry.util.http import get_excluded_urls, get_traced_request_attrs from opentelemetry.util.providers import time_ns @@ -108,9 +109,7 @@ def __call__(self, env, start_response): start_time = time_ns() - token = context.attach( - propagators.extract(otel_wsgi.carrier_getter, env) - ) + token = context.attach(extract(otel_wsgi.carrier_getter, env)) span = self._tracer.start_span( otel_wsgi.get_default_span_name(env), kind=trace.SpanKind.SERVER, diff --git a/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/__init__.py b/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/__init__.py index 43ffb80d43..67a13907a3 100644 --- a/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/__init__.py @@ -52,9 +52,10 @@ def hello(): import flask import opentelemetry.instrumentation.wsgi as otel_wsgi -from opentelemetry import context, propagators, trace +from opentelemetry import context, trace from opentelemetry.instrumentation.flask.version import __version__ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor +from opentelemetry.propagate import extract from opentelemetry.util.http import get_excluded_urls from opentelemetry.util.providers import time_ns @@ -116,9 +117,7 @@ def _before_request(): flask_request_environ = flask.request.environ span_name = name_callback() token = context.attach( - propagators.extract( - otel_wsgi.carrier_getter, flask_request_environ - ) + extract(otel_wsgi.carrier_getter, flask_request_environ) ) tracer = trace.get_tracer(__name__, __version__) diff --git a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_client.py b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_client.py index c69c15f563..fd4d79da21 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_client.py +++ b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_client.py @@ -24,9 +24,10 @@ import grpc -from opentelemetry import propagators, trace +from opentelemetry import trace from opentelemetry.instrumentation.grpc import grpcext from opentelemetry.instrumentation.grpc._utilities import RpcInfo +from opentelemetry.propagate import inject from opentelemetry.trace.status import Status, StatusCode @@ -59,7 +60,7 @@ def append_metadata( metadata[key] = value # Inject current active span from the context - propagators.inject(append_metadata, metadata) + inject(append_metadata, metadata) def _make_future_done_callback(span, rpc_info): diff --git a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_server.py b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_server.py index 9895b8853f..3be03e03dc 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_server.py +++ b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_server.py @@ -26,8 +26,9 @@ import grpc -from opentelemetry import propagators, trace +from opentelemetry import trace from opentelemetry.context import attach, detach +from opentelemetry.propagate import extract from opentelemetry.trace.propagation.textmap import DictGetter from opentelemetry.trace.status import Status, StatusCode @@ -181,7 +182,7 @@ def _set_remote_context(self, servicer_context): metadata = servicer_context.invocation_metadata() if metadata: md_dict = {md.key: md.value for md in metadata} - ctx = propagators.extract(self._carrier_getter, md_dict) + ctx = extract(self._carrier_getter, md_dict) token = attach(ctx) try: yield diff --git a/instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/callbacks.py b/instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/callbacks.py index 69b07e55ba..b5ee1bbdcd 100644 --- a/instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/callbacks.py +++ b/instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/callbacks.py @@ -6,8 +6,9 @@ from pyramid.tweens import EXCVIEW import opentelemetry.instrumentation.wsgi as otel_wsgi -from opentelemetry import context, propagators, trace +from opentelemetry import context, trace from opentelemetry.instrumentation.pyramid.version import __version__ +from opentelemetry.propagate import extract from opentelemetry.util.http import get_excluded_urls from opentelemetry.util.providers import time_ns @@ -63,9 +64,7 @@ def _before_traversal(event): start_time = request_environ.get(_ENVIRON_STARTTIME_KEY) - token = context.attach( - propagators.extract(otel_wsgi.carrier_getter, request_environ) - ) + token = context.attach(extract(otel_wsgi.carrier_getter, request_environ)) tracer = trace.get_tracer(__name__, __version__) if request.matched_route: diff --git a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py index b44468e763..9a3fdfcd4b 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py @@ -36,18 +36,17 @@ import functools import types -from requests import Timeout, URLRequired -from requests.exceptions import InvalidSchema, InvalidURL, MissingSchema from requests.models import Response from requests.sessions import Session from requests.structures import CaseInsensitiveDict -from opentelemetry import context, propagators +from opentelemetry import context from opentelemetry.instrumentation.instrumentor import BaseInstrumentor from opentelemetry.instrumentation.requests.version import __version__ from opentelemetry.instrumentation.utils import http_status_to_status_code +from opentelemetry.propagate import inject from opentelemetry.trace import SpanKind, get_tracer -from opentelemetry.trace.status import Status, StatusCode +from opentelemetry.trace.status import Status # A key to a context variable to avoid creating duplicate spans when instrumenting # both, Session.request and Session.send, since Session.request calls into Session.send @@ -135,7 +134,7 @@ def _instrumented_requests_call( span.set_attribute("http.url", url) headers = get_or_create_headers() - propagators.inject(type(headers).__setitem__, headers) + inject(type(headers).__setitem__, headers) token = context.attach( context.set_value(_SUPPRESS_REQUESTS_INSTRUMENTATION_KEY, True) diff --git a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py index 3763124981..aed47238fb 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py +++ b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py @@ -19,8 +19,9 @@ import requests import opentelemetry.instrumentation.requests -from opentelemetry import context, propagators, trace +from opentelemetry import context, trace from opentelemetry.instrumentation.requests import RequestsInstrumentor +from opentelemetry.propagate import get_global_textmap, set_global_textmap from opentelemetry.sdk import resources from opentelemetry.test.mock_textmap import MockTextMapPropagator from opentelemetry.test.test_base import TestBase @@ -191,9 +192,9 @@ def test_not_recording(self): self.assertFalse(mock_span.set_status.called) def test_distributed_context(self): - previous_propagator = propagators.get_global_textmap() + previous_propagator = get_global_textmap() try: - propagators.set_global_textmap(MockTextMapPropagator()) + set_global_textmap(MockTextMapPropagator()) result = self.perform_request(self.URL) self.assertEqual(result.text, "Hello!") @@ -212,7 +213,7 @@ def test_distributed_context(self): ) finally: - propagators.set_global_textmap(previous_propagator) + set_global_textmap(previous_propagator) def test_span_callback(self): RequestsInstrumentor().uninstrument() diff --git a/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py b/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py index 3eaf47e62b..01a2433d7e 100644 --- a/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py @@ -43,7 +43,7 @@ def get(self): import wrapt from wrapt import wrap_function_wrapper -from opentelemetry import context, propagators, trace +from opentelemetry import context, trace from opentelemetry.instrumentation.instrumentor import BaseInstrumentor from opentelemetry.instrumentation.tornado.version import __version__ from opentelemetry.instrumentation.utils import ( @@ -51,6 +51,7 @@ def get(self): http_status_to_status_code, unwrap, ) +from opentelemetry.propagate import extract from opentelemetry.trace.propagation.textmap import DictGetter from opentelemetry.trace.status import Status from opentelemetry.util.http import get_excluded_urls, get_traced_request_attrs @@ -195,9 +196,7 @@ def _get_operation_name(handler, request): def _start_span(tracer, handler, start_time) -> _TraceContext: - token = context.attach( - propagators.extract(carrier_getter, handler.request.headers,) - ) + token = context.attach(extract(carrier_getter, handler.request.headers,)) span = tracer.start_span( _get_operation_name(handler, handler.request), diff --git a/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/client.py b/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/client.py index 9df00bce35..65f8361ce5 100644 --- a/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/client.py +++ b/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/client.py @@ -2,8 +2,9 @@ from tornado.httpclient import HTTPError, HTTPRequest -from opentelemetry import propagators, trace +from opentelemetry import trace from opentelemetry.instrumentation.utils import http_status_to_status_code +from opentelemetry.propagate import inject from opentelemetry.trace.status import Status from opentelemetry.util.providers import time_ns @@ -50,7 +51,7 @@ def fetch_async(tracer, func, _, args, kwargs): span.set_attribute(key, value) with tracer.use_span(span): - propagators.inject(type(request.headers).__setitem__, request.headers) + inject(type(request.headers).__setitem__, request.headers) future = func(*args, **kwargs) future.add_done_callback( functools.partial(_finish_tracing_callback, span=span) diff --git a/instrumentation/opentelemetry-instrumentation-urllib/src/opentelemetry/instrumentation/urllib/__init__.py b/instrumentation/opentelemetry-instrumentation-urllib/src/opentelemetry/instrumentation/urllib/__init__.py index cf3bbc7f52..1c305748b5 100644 --- a/instrumentation/opentelemetry-instrumentation-urllib/src/opentelemetry/instrumentation/urllib/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-urllib/src/opentelemetry/instrumentation/urllib/__init__.py @@ -43,14 +43,15 @@ Request, ) -from opentelemetry import context, propagators +from opentelemetry import context from opentelemetry.instrumentation.instrumentor import BaseInstrumentor from opentelemetry.instrumentation.urllib.version import ( # pylint: disable=no-name-in-module,import-error __version__, ) from opentelemetry.instrumentation.utils import http_status_to_status_code +from opentelemetry.propagate import inject from opentelemetry.trace import SpanKind, get_tracer -from opentelemetry.trace.status import Status, StatusCode +from opentelemetry.trace.status import Status # A key to a context variable to avoid creating duplicate spans when instrumenting _SUPPRESS_URLLIB_INSTRUMENTATION_KEY = "suppress_urllib_instrumentation" @@ -150,7 +151,7 @@ def _instrumented_open_call( span.set_attribute("http.url", url) headers = get_or_create_headers() - propagators.inject(type(headers).__setitem__, headers) + inject(type(headers).__setitem__, headers) token = context.attach( context.set_value(_SUPPRESS_URLLIB_INSTRUMENTATION_KEY, True) diff --git a/instrumentation/opentelemetry-instrumentation-urllib/tests/test_urllib_integration.py b/instrumentation/opentelemetry-instrumentation-urllib/tests/test_urllib_integration.py index da01f97b73..5e4e97885f 100644 --- a/instrumentation/opentelemetry-instrumentation-urllib/tests/test_urllib_integration.py +++ b/instrumentation/opentelemetry-instrumentation-urllib/tests/test_urllib_integration.py @@ -24,10 +24,11 @@ import httpretty import opentelemetry.instrumentation.urllib # pylint: disable=no-name-in-module,import-error -from opentelemetry import context, propagators, trace +from opentelemetry import context, trace from opentelemetry.instrumentation.urllib import ( # pylint: disable=no-name-in-module,import-error URLLibInstrumentor, ) +from opentelemetry.propagate import get_global_textmap, set_global_textmap from opentelemetry.sdk import resources from opentelemetry.test.mock_textmap import MockTextMapPropagator from opentelemetry.test.test_base import TestBase @@ -222,9 +223,9 @@ def test_not_recording(self): self.assertFalse(mock_span.set_status.called) def test_distributed_context(self): - previous_propagator = propagators.get_global_textmap() + previous_propagator = get_global_textmap() try: - propagators.set_global_textmap(MockTextMapPropagator()) + set_global_textmap(MockTextMapPropagator()) result = self.perform_request(self.URL) self.assertEqual(result.read(), b"Hello!") @@ -247,7 +248,7 @@ def test_distributed_context(self): ) finally: - propagators.set_global_textmap(previous_propagator) + set_global_textmap(previous_propagator) def test_span_callback(self): URLLibInstrumentor().uninstrument() diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index c336a1320b..0f81f3e9a0 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -58,9 +58,10 @@ def hello(): import typing import wsgiref.util as wsgiref_util -from opentelemetry import context, propagators, trace +from opentelemetry import context, trace from opentelemetry.instrumentation.utils import http_status_to_status_code from opentelemetry.instrumentation.wsgi.version import __version__ +from opentelemetry.propagate import extract from opentelemetry.trace.propagation.textmap import DictGetter from opentelemetry.trace.status import Status, StatusCode @@ -207,7 +208,7 @@ def __call__(self, environ, start_response): start_response: The WSGI start_response callable. """ - token = context.attach(propagators.extract(carrier_getter, environ)) + token = context.attach(extract(carrier_getter, environ)) span_name = self.name_callback(environ) span = self.tracer.start_span( diff --git a/propagator/opentelemetry-propagator-ot-trace/README.rst b/propagator/opentelemetry-propagator-ot-trace/README.rst new file mode 100644 index 0000000000..9d8dea482f --- /dev/null +++ b/propagator/opentelemetry-propagator-ot-trace/README.rst @@ -0,0 +1,48 @@ +OpenTelemetry OT Trace Propagator +================================= + +|pypi| + +.. |pypi| image:: https://badge.fury.io/py/opentelemetry-propagator-ot-trace.svg + :target: https://pypi.org/project/opentelemetry-propagator-ot-trace/ + +Installation +------------ + +:: + + pip install opentelemetry-propagator-ot-trace + +.. _OpenTelemetry: https://github.com/open-telemetry/opentelemetry-python/ + +OTTrace Format +-------------- + +So far there is no "formal" specification of the OTTrace format. The best +document that servers this purpose that exists now is this_ implementation. + +.. _this: https://github.com/opentracing/basictracer-python/blob/master/basictracer/text_propagator.py + +===================== ======================================================================================================================================= ===================== +Header Name Description Required +===================== ======================================================================================================================================= ===================== +``ot-tracer-traceid`` uint64 encoded as a string of 16 hex characters yes +``ot-tracer-spanid`` uint64 encoded as a string of 16 hex characters yes +``ot-tracer-sampled`` boolean encoded as a string with the values ``true`` or ``false`` no +``ot-baggage-*`` repeated string to string key-value baggage items; keys are prefixed with ``ot-baggage-`` and the corresponding value is the raw string if baggage is present +===================== ======================================================================================================================================= ===================== + +Interop and trace ids +--------------------- + +The OT Trace propagation format expects trace ids to be 64-bits. In order to +interop with OpenTelemetry, trace ids need to be truncated to 64-bits before +sending them on the wire. When truncating, the least significant (right-most) +bits MUST be retained. For example, a trace id of +``3c3039f4d78d5c02ee8e3e41b17ce105`` would be truncated to +``ee8e3e41b17ce105``. + +References +---------- + +* `OpenTelemetry Project `_ diff --git a/propagator/opentelemetry-propagator-ot-trace/setup.cfg b/propagator/opentelemetry-propagator-ot-trace/setup.cfg new file mode 100644 index 0000000000..f71c61f2b5 --- /dev/null +++ b/propagator/opentelemetry-propagator-ot-trace/setup.cfg @@ -0,0 +1,53 @@ +# 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. +# +[metadata] +name = opentelemetry-propagator-ot-trace +description = OT Trace Propagator for OpenTelemetry +long_description = file: README.rst +long_description_content_type = text/x-rst +author = OpenTelemetry Authors +author_email = cncf-opentelemetry-contributors@lists.cncf.io +url = https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/propagator/opentelemetry-propagator-ot-trace +platforms = any +license = Apache-2.0 +classifiers = + Development Status :: 4 - Beta + Intended Audience :: Developers + License :: OSI Approved :: Apache Software License + Programming Language :: Python + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.5 + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + +[options] +python_requires = >=3.5 +package_dir= + =src +packages=find_namespace: +install_requires = + opentelemetry-api == 1.0.0.dev0 + opentelemetry-sdk == 1.0.0.dev0 + +[options.packages.find] +where = src + +[options.extras_require] +test = + +[options.entry_points] +opentelemetry_propagator = + ottrace = opentelemetry.propagators.OTTracePropagator diff --git a/propagator/opentelemetry-propagator-ot-trace/setup.py b/propagator/opentelemetry-propagator-ot-trace/setup.py new file mode 100644 index 0000000000..a408e061c4 --- /dev/null +++ b/propagator/opentelemetry-propagator-ot-trace/setup.py @@ -0,0 +1,27 @@ +# 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. + +import os + +import setuptools + +BASE_DIR = os.path.dirname(__file__) +VERSION_FILENAME = os.path.join( + BASE_DIR, "src", "opentelemetry", "propagators", "ot_trace", "version.py" +) +PACKAGE_INFO = {} +with open(VERSION_FILENAME) as f: + exec(f.read(), PACKAGE_INFO) + +setuptools.setup(version=PACKAGE_INFO["__version__"]) diff --git a/propagator/opentelemetry-propagator-ot-trace/src/opentelemetry/propagators/ot_trace/__init__.py b/propagator/opentelemetry-propagator-ot-trace/src/opentelemetry/propagators/ot_trace/__init__.py new file mode 100644 index 0000000000..b7e9cf5449 --- /dev/null +++ b/propagator/opentelemetry-propagator-ot-trace/src/opentelemetry/propagators/ot_trace/__init__.py @@ -0,0 +1,170 @@ +# 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 re import compile as re_compile +from typing import Iterable, Optional + +from opentelemetry.baggage import get_all, set_baggage +from opentelemetry.context import Context +from opentelemetry.trace import ( + INVALID_SPAN_ID, + INVALID_TRACE_ID, + DefaultSpan, + SpanContext, + TraceFlags, + get_current_span, + set_span_in_context, +) +from opentelemetry.trace.propagation.textmap import ( + Getter, + Setter, + TextMapPropagator, + TextMapPropagatorT, +) + +OT_TRACE_ID_HEADER = "ot-tracer-traceid" +OT_SPAN_ID_HEADER = "ot-tracer-spanid" +OT_SAMPLED_HEADER = "ot-tracer-sampled" +OT_BAGGAGE_PREFIX = "ot-baggage-" + +_valid_header_name = re_compile(r"[\w_^`!#$%&'*+.|~]+") +_valid_header_value = re_compile(r"[\t\x20-\x7e\x80-\xff]+") +_valid_extract_traceid = re_compile(r"[0-9a-f]{1,32}") +_valid_extract_spanid = re_compile(r"[0-9a-f]{1,16}") + + +class OTTracePropagator(TextMapPropagator): + """Propagator for the OTTrace HTTP header format""" + + def extract( + self, + getter: Getter[TextMapPropagatorT], + carrier: TextMapPropagatorT, + context: Optional[Context] = None, + ) -> Context: + + traceid = _extract_first_element( + getter.get(carrier, OT_TRACE_ID_HEADER) + ) + + spanid = _extract_first_element(getter.get(carrier, OT_SPAN_ID_HEADER)) + + sampled = _extract_first_element( + getter.get(carrier, OT_SAMPLED_HEADER) + ) + + if sampled == "true": + traceflags = TraceFlags.SAMPLED + else: + traceflags = TraceFlags.DEFAULT + + if ( + traceid != INVALID_TRACE_ID + and _valid_extract_traceid.fullmatch(traceid) is not None + and spanid != INVALID_SPAN_ID + and _valid_extract_spanid.fullmatch(spanid) is not None + ): + context = set_span_in_context( + DefaultSpan( + SpanContext( + trace_id=int(traceid, 16), + span_id=int(spanid, 16), + is_remote=True, + trace_flags=traceflags, + ) + ), + context, + ) + + baggage = get_all(context) or {} + + for key in getter.keys(carrier): + + if not key.startswith(OT_BAGGAGE_PREFIX): + continue + + baggage[ + key[len(OT_BAGGAGE_PREFIX) :] + ] = _extract_first_element(getter.get(carrier, key)) + + for key, value in baggage.items(): + context = set_baggage(key, value, context) + + return context + + def inject( + self, + set_in_carrier: Setter[TextMapPropagatorT], + carrier: TextMapPropagatorT, + context: Optional[Context] = None, + ) -> None: + + span_context = get_current_span(context).get_span_context() + + if span_context.trace_id == INVALID_TRACE_ID: + return + + set_in_carrier( + carrier, OT_TRACE_ID_HEADER, hex(span_context.trace_id)[2:][-16:] + ) + set_in_carrier( + carrier, OT_SPAN_ID_HEADER, hex(span_context.span_id)[2:][-16:], + ) + + if span_context.trace_flags == TraceFlags.SAMPLED: + traceflags = "true" + else: + traceflags = "false" + + set_in_carrier(carrier, OT_SAMPLED_HEADER, traceflags) + + baggage = get_all(context) + + if not baggage: + return + + for header_name, header_value in baggage.items(): + + if ( + _valid_header_name.fullmatch(header_name) is None + or _valid_header_value.fullmatch(header_value) is None + ): + continue + + set_in_carrier( + carrier, + "".join([OT_BAGGAGE_PREFIX, header_name]), + header_value, + ) + + @property + def fields(self): + """Returns a set with the fields set in `inject`. + + See + `opentelemetry.trace.propagation.textmap.TextMapPropagator.fields` + """ + return { + OT_TRACE_ID_HEADER, + OT_SPAN_ID_HEADER, + OT_SAMPLED_HEADER, + } + + +def _extract_first_element( + items: Iterable[TextMapPropagatorT], +) -> Optional[TextMapPropagatorT]: + if items is None: + return None + return next(iter(items), None) diff --git a/propagator/opentelemetry-propagator-ot-trace/src/opentelemetry/propagators/ot_trace/version.py b/propagator/opentelemetry-propagator-ot-trace/src/opentelemetry/propagators/ot_trace/version.py new file mode 100644 index 0000000000..ebb75f6c11 --- /dev/null +++ b/propagator/opentelemetry-propagator-ot-trace/src/opentelemetry/propagators/ot_trace/version.py @@ -0,0 +1,15 @@ +# 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. + +__version__ = "0.18.dev0" diff --git a/propagator/opentelemetry-propagator-ot-trace/tests/__init__.py b/propagator/opentelemetry-propagator-ot-trace/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/propagator/opentelemetry-propagator-ot-trace/tests/test_ot_trace_propagator.py b/propagator/opentelemetry-propagator-ot-trace/tests/test_ot_trace_propagator.py new file mode 100644 index 0000000000..3c4cd7c166 --- /dev/null +++ b/propagator/opentelemetry-propagator-ot-trace/tests/test_ot_trace_propagator.py @@ -0,0 +1,368 @@ +# 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 unittest import TestCase + +from opentelemetry.baggage import get_all, set_baggage +from opentelemetry.propagators.ot_trace import ( + OT_BAGGAGE_PREFIX, + OT_SAMPLED_HEADER, + OT_SPAN_ID_HEADER, + OT_TRACE_ID_HEADER, + OTTracePropagator, +) +from opentelemetry.sdk.trace import _Span +from opentelemetry.trace import ( + INVALID_SPAN_CONTEXT, + INVALID_SPAN_ID, + INVALID_TRACE_ID, + SpanContext, + TraceFlags, + set_span_in_context, +) +from opentelemetry.trace.propagation import get_current_span +from opentelemetry.trace.propagation.textmap import DictGetter + +carrier_getter = DictGetter() + + +class TestOTTracePropagator(TestCase): + + ot_trace_propagator = OTTracePropagator() + + def carrier_inject(self, trace_id, span_id, is_remote, trace_flags): + + carrier = {} + + self.ot_trace_propagator.inject( + dict.__setitem__, + carrier, + set_span_in_context( + _Span( + "child", + context=SpanContext( + trace_id=trace_id, + span_id=span_id, + is_remote=is_remote, + trace_flags=trace_flags, + ), + ) + ), + ) + + return carrier + + def test_inject_short_trace_id_short_span_id(self): + carrier = self.carrier_inject( + int("1", 16), int("2", 16), True, TraceFlags.SAMPLED, + ) + + self.assertEqual(carrier[OT_TRACE_ID_HEADER], "1") + self.assertEqual(carrier[OT_SPAN_ID_HEADER], "2") + + def test_inject_trace_id_span_id_true(self): + """Test valid trace_id, span_id and sampled true""" + carrier = self.carrier_inject( + int("80f198ee56343ba864fe8b2a57d3eff7", 16), + int("e457b5a2e4d86bd1", 16), + True, + TraceFlags.SAMPLED, + ) + + self.assertEqual(carrier[OT_TRACE_ID_HEADER], "64fe8b2a57d3eff7") + self.assertEqual(carrier[OT_SPAN_ID_HEADER], "e457b5a2e4d86bd1") + self.assertEqual(carrier[OT_SAMPLED_HEADER], "true") + + def test_inject_trace_id_span_id_false(self): + """Test valid trace_id, span_id and sampled true""" + carrier = self.carrier_inject( + int("80f198ee56343ba864fe8b2a57d3eff7", 16), + int("e457b5a2e4d86bd1", 16), + False, + TraceFlags.DEFAULT, + ) + + self.assertEqual(carrier[OT_TRACE_ID_HEADER], "64fe8b2a57d3eff7") + self.assertEqual(carrier[OT_SPAN_ID_HEADER], "e457b5a2e4d86bd1") + self.assertEqual(carrier[OT_SAMPLED_HEADER], "false") + + def test_inject_truncate_traceid(self): + """Test that traceid is truncated to 64 bits""" + + self.assertEqual( + self.carrier_inject( + int("80f198ee56343ba864fe8b2a57d3eff7", 16), + int("e457b5a2e4d86bd1", 16), + True, + TraceFlags.DEFAULT, + )[OT_TRACE_ID_HEADER], + "64fe8b2a57d3eff7", + ) + + def test_inject_sampled_true(self): + """Test that sampled true trace flags are injected""" + + self.assertEqual( + self.carrier_inject( + int("80f198ee56343ba864fe8b2a57d3eff7", 16), + int("e457b5a2e4d86bd1", 16), + True, + TraceFlags.SAMPLED, + )[OT_SAMPLED_HEADER], + "true", + ) + + def test_inject_sampled_false(self): + """Test that sampled false trace flags are injected""" + + self.assertEqual( + self.carrier_inject( + int("80f198ee56343ba864fe8b2a57d3eff7", 16), + int("e457b5a2e4d86bd1", 16), + True, + TraceFlags.DEFAULT, + )[OT_SAMPLED_HEADER], + "false", + ) + + def test_inject_invalid_trace_id(self): + """Test that no attributes are injected if the trace_id is invalid""" + + self.assertEqual( + self.carrier_inject( + INVALID_TRACE_ID, + int("e457b5a2e4d86bd1", 16), + True, + TraceFlags.SAMPLED, + ), + {}, + ) + + def test_inject_set_baggage(self): + """Test that baggage is set""" + + carrier = {} + + self.ot_trace_propagator.inject( + dict.__setitem__, + carrier, + set_baggage( + "key", + "value", + context=set_span_in_context( + _Span( + "child", + SpanContext( + trace_id=int( + "80f198ee56343ba864fe8b2a57d3eff7", 16 + ), + span_id=int("e457b5a2e4d86bd1", 16), + is_remote=True, + trace_flags=TraceFlags.SAMPLED, + ), + ) + ), + ), + ) + + self.assertEqual(carrier["".join([OT_BAGGAGE_PREFIX, "key"])], "value") + + def test_inject_invalid_baggage_keys(self): + """Test that invalid baggage keys are not set""" + + carrier = {} + + self.ot_trace_propagator.inject( + dict.__setitem__, + carrier, + set_baggage( + "(", + "value", + context=set_span_in_context( + _Span( + "child", + SpanContext( + trace_id=int( + "80f198ee56343ba864fe8b2a57d3eff7", 16 + ), + span_id=int("e457b5a2e4d86bd1", 16), + is_remote=True, + trace_flags=TraceFlags.SAMPLED, + ), + ) + ), + ), + ) + + self.assertNotIn("".join([OT_BAGGAGE_PREFIX, "!"]), carrier.keys()) + + def test_inject_invalid_baggage_values(self): + """Test that invalid baggage values are not set""" + + carrier = {} + + self.ot_trace_propagator.inject( + dict.__setitem__, + carrier, + set_baggage( + "key", + "α", + context=set_span_in_context( + _Span( + "child", + SpanContext( + trace_id=int( + "80f198ee56343ba864fe8b2a57d3eff7", 16 + ), + span_id=int("e457b5a2e4d86bd1", 16), + is_remote=True, + trace_flags=TraceFlags.SAMPLED, + ), + ) + ), + ), + ) + + self.assertNotIn("".join([OT_BAGGAGE_PREFIX, "key"]), carrier.keys()) + + def test_extract_trace_id_span_id_sampled_true(self): + """Test valid trace_id, span_id and sampled true""" + + span_context = get_current_span( + self.ot_trace_propagator.extract( + carrier_getter, + { + OT_TRACE_ID_HEADER: "80f198ee56343ba864fe8b2a57d3eff7", + OT_SPAN_ID_HEADER: "e457b5a2e4d86bd1", + OT_SAMPLED_HEADER: "true", + }, + ) + ).get_span_context() + + self.assertEqual( + hex(span_context.trace_id)[2:], "80f198ee56343ba864fe8b2a57d3eff7" + ) + self.assertEqual(hex(span_context.span_id)[2:], "e457b5a2e4d86bd1") + self.assertTrue(span_context.is_remote) + self.assertEqual(span_context.trace_flags, TraceFlags.SAMPLED) + + def test_extract_trace_id_span_id_sampled_false(self): + """Test valid trace_id, span_id and sampled false""" + + span_context = get_current_span( + self.ot_trace_propagator.extract( + carrier_getter, + { + OT_TRACE_ID_HEADER: "80f198ee56343ba864fe8b2a57d3eff7", + OT_SPAN_ID_HEADER: "e457b5a2e4d86bd1", + OT_SAMPLED_HEADER: "false", + }, + ) + ).get_span_context() + + self.assertEqual( + hex(span_context.trace_id)[2:], "80f198ee56343ba864fe8b2a57d3eff7" + ) + self.assertEqual(hex(span_context.span_id)[2:], "e457b5a2e4d86bd1") + self.assertTrue(span_context.is_remote) + self.assertEqual(span_context.trace_flags, TraceFlags.DEFAULT) + + def test_extract_malformed_trace_id(self): + """Test extraction with malformed trace_id""" + + span_context = get_current_span( + self.ot_trace_propagator.extract( + carrier_getter, + { + OT_TRACE_ID_HEADER: "abc123!", + OT_SPAN_ID_HEADER: "e457b5a2e4d86bd1", + OT_SAMPLED_HEADER: "false", + }, + ) + ).get_span_context() + + self.assertEqual(span_context, INVALID_SPAN_CONTEXT) + + def test_extract_malformed_span_id(self): + """Test extraction with malformed span_id""" + + span_context = get_current_span( + self.ot_trace_propagator.extract( + carrier_getter, + { + OT_TRACE_ID_HEADER: "64fe8b2a57d3eff7", + OT_SPAN_ID_HEADER: "abc123!", + OT_SAMPLED_HEADER: "false", + }, + ) + ).get_span_context() + + self.assertEqual(span_context, INVALID_SPAN_CONTEXT) + + def test_extract_invalid_trace_id(self): + """Test extraction with invalid trace_id""" + + span_context = get_current_span( + self.ot_trace_propagator.extract( + carrier_getter, + { + OT_TRACE_ID_HEADER: INVALID_TRACE_ID, + OT_SPAN_ID_HEADER: "e457b5a2e4d86bd1", + OT_SAMPLED_HEADER: "false", + }, + ) + ).get_span_context() + + self.assertEqual(span_context, INVALID_SPAN_CONTEXT) + + def test_extract_invalid_span_id(self): + """Test extraction with invalid span_id""" + + span_context = get_current_span( + self.ot_trace_propagator.extract( + carrier_getter, + { + OT_TRACE_ID_HEADER: "64fe8b2a57d3eff7", + OT_SPAN_ID_HEADER: INVALID_SPAN_ID, + OT_SAMPLED_HEADER: "false", + }, + ) + ).get_span_context() + + self.assertEqual(span_context, INVALID_SPAN_CONTEXT) + + def test_extract_baggage(self): + """Test baggage extraction""" + + context = self.ot_trace_propagator.extract( + carrier_getter, + { + OT_TRACE_ID_HEADER: "64fe8b2a57d3eff7", + OT_SPAN_ID_HEADER: "e457b5a2e4d86bd1", + OT_SAMPLED_HEADER: "false", + "".join([OT_BAGGAGE_PREFIX, "abc"]): "abc", + "".join([OT_BAGGAGE_PREFIX, "def"]): "def", + }, + ) + span_context = get_current_span(context).get_span_context() + + self.assertEqual(hex(span_context.trace_id)[2:], "64fe8b2a57d3eff7") + self.assertEqual(hex(span_context.span_id)[2:], "e457b5a2e4d86bd1") + self.assertTrue(span_context.is_remote) + self.assertEqual(span_context.trace_flags, TraceFlags.DEFAULT) + + baggage = get_all(context) + + self.assertEqual(baggage["abc"], "abc") + self.assertEqual(baggage["def"], "def") diff --git a/sdk-extension/opentelemetry-sdk-extension-aws/README.rst b/sdk-extension/opentelemetry-sdk-extension-aws/README.rst index 2c74a65271..42c837ae14 100644 --- a/sdk-extension/opentelemetry-sdk-extension-aws/README.rst +++ b/sdk-extension/opentelemetry-sdk-extension-aws/README.rst @@ -60,10 +60,10 @@ Or by setting this propagator in your instrumented application: .. code-block:: python - from opentelemetry import propagators + from opentelemetry.propagate import set_global_textmap from opentelemetry.sdk.extension.aws.trace.propagation.aws_xray_format import AwsXRayFormat - propagators.set_global_textmap(AwsXRayFormat()) + set_global_textmap(AwsXRayFormat()) References ---------- diff --git a/sdk-extension/opentelemetry-sdk-extension-aws/src/opentelemetry/sdk/extension/aws/trace/propagation/aws_xray_format.py b/sdk-extension/opentelemetry-sdk-extension-aws/src/opentelemetry/sdk/extension/aws/trace/propagation/aws_xray_format.py index 63ccb00206..16d41f7d41 100644 --- a/sdk-extension/opentelemetry-sdk-extension-aws/src/opentelemetry/sdk/extension/aws/trace/propagation/aws_xray_format.py +++ b/sdk-extension/opentelemetry-sdk-extension-aws/src/opentelemetry/sdk/extension/aws/trace/propagation/aws_xray_format.py @@ -38,10 +38,10 @@ .. code-block:: python - from opentelemetry import propagators + from opentelemetry.propagate import set_global_textmap from opentelemetry.sdk.extension.aws.trace.propagation.aws_xray_format import AwsXRayFormat - propagators.set_global_textmap(AwsXRayFormat()) + set_global_textmap(AwsXRayFormat()) API --- diff --git a/tox.ini b/tox.ini index 6a2e9c4b44..b155b988ad 100644 --- a/tox.ini +++ b/tox.ini @@ -137,6 +137,10 @@ envlist = py3{5,6,7,8}-test-util-http pypy3-test-util-http + ; opentelemetry-propagator-ot-trace + py3{5,6,7,8}-test-propagator-ot-trace + pypy3-test-propagator-ot-trace + lint docker-tests docs @@ -193,6 +197,7 @@ changedir = test-instrumentation-wsgi: instrumentation/opentelemetry-instrumentation-wsgi/tests test-util-http: util/opentelemetry-util-http/tests test-sdkextension-aws: sdk-extension/opentelemetry-sdk-extension-aws/tests + test-propagator-ot-trace: propagator/opentelemetry-propagator-ot-trace/tests test-exporter-datadog: exporter/opentelemetry-exporter-datadog/tests @@ -273,6 +278,8 @@ commands_pre = http: pip install {toxinidir}/util/opentelemetry-util-http ; In order to get a health coverage report, + propagator-ot-trace: pip install {toxinidir}/propagator/opentelemetry-propagator-ot-trace[test] + ; we have to install packages in editable mode. coverage: python {toxinidir}/scripts/eachdist.py install --editable @@ -349,6 +356,7 @@ commands_pre = python -m pip install -e {toxinidir}/instrumentation/opentelemetry-instrumentation-mysql[test] python -m pip install -e {toxinidir}/exporter/opentelemetry-exporter-datadog[test] python -m pip install -e {toxinidir}/sdk-extension/opentelemetry-sdk-extension-aws[test] + python -m pip install -e {toxinidir}/propagator/opentelemetry-propagator-ot-trace[test] commands = python scripts/eachdist.py lint --check-only