diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1f367d85eaa..eb6b0b5b93e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,13 +6,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased](https://github.com/open-telemetry/opentelemetry-python/compare/v1.1.0...HEAD)
+### Changed
+- Include span parent in Jaeger gRPC export as `CHILD_OF` reference
+ ([#1809])(https://github.com/open-telemetry/opentelemetry-python/pull/1809)
+
### Added
- Added example for running Django with auto instrumentation.
([#1803](https://github.com/open-telemetry/opentelemetry-python/pull/1803))
+- Added support for OTEL_SERVICE_NAME.
+ ([#1829](https://github.com/open-telemetry/opentelemetry-python/pull/1829))
### Changed
- Fixed OTLP gRPC exporter silently failing if scheme is not specified in endpoint.
([#1806](https://github.com/open-telemetry/opentelemetry-python/pull/1806))
+- Rename CompositeHTTPPropagator to CompositePropagator as per specification.
+ ([#1807](https://github.com/open-telemetry/opentelemetry-python/pull/1807))
+- Propagators use the root context as default for `extract` and do not modify
+ the context if extracting from carrier does not work.
+ ([#1811](https://github.com/open-telemetry/opentelemetry-python/pull/1811))
+- Improve warning when failing to decode byte attribute
+ ([#1810](https://github.com/open-telemetry/opentelemetry-python/pull/1810))
+- Fixed inconsistency in parent_id formatting from the ConsoleSpanExporter
+ ([#1833](https://github.com/open-telemetry/opentelemetry-python/pull/1833))
### Removed
- Moved `opentelemetry-instrumentation` to contrib repository.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 6b6918ec30d..9c6e72244de 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -19,7 +19,7 @@ This is the main repo for OpenTelemetry Python. Nevertheless, there are other re
Please take a look at this list first, your contributions may belong in one of these repos better:
1. [OpenTelemetry Contrib](https://github.com/open-telemetry/opentelemetry-python-contrib): Instrumentations for third-party
- libraries and frameworks. There is an ongoing effort to migrate into the Opentelemetry Contrib repo some of the existing
+ libraries and frameworks. There is an ongoing effort to migrate into the OpenTelemetry Contrib repo some of the existing
programmatic instrumentations that are now in the `ext` directory in the main OpenTelemetry repo. Please ask in the Slack
channel (see below) for guidance if you want to contribute with these instrumentations.
diff --git a/README.md b/README.md
index 2e8d6c887ac..e6a174e8848 100644
--- a/README.md
+++ b/README.md
@@ -132,7 +132,6 @@ Approvers ([@open-telemetry/python-approvers](https://github.com/orgs/open-telem
- [Aaron Abbott](https://github.com/aabmass), Google
- [Diego Hurtado](https://github.com/ocelotl)
-- [Hector Hernandez](https://github.com/hectorhdzg), Microsoft
- [Owais Lone](https://github.com/owais), Splunk
- [Srikanth Chekuri](https://github.com/lonewolf3739)
diff --git a/dev-requirements.txt b/dev-requirements.txt
index bcbc90148c1..2fb15652b4f 100644
--- a/dev-requirements.txt
+++ b/dev-requirements.txt
@@ -4,9 +4,9 @@ isort~=5.8
black~=20.8b1
httpretty~=1.0
mypy==0.812
-sphinx~=2.1
-sphinx-rtd-theme~=0.4
-sphinx-autodoc-typehints~=1.10.2
+sphinx~=3.5.4
+sphinx-rtd-theme~=0.5
+sphinx-autodoc-typehints
pytest>=6.0
pytest-cov>=2.8
readme-renderer~=24.0
diff --git a/docs-requirements.txt b/docs-requirements.txt
index f802674afd0..f60325f00b1 100644
--- a/docs-requirements.txt
+++ b/docs-requirements.txt
@@ -1,6 +1,8 @@
-sphinx~=2.4
-sphinx-rtd-theme~=0.4
+sphinx~=3.5.4
+sphinx-rtd-theme~=0.5
sphinx-autodoc-typehints
+# used to generate docs for the website
+sphinx-jekyll-builder
# Need to install the api/sdk in the venv for autodoc. Modifying sys.path
# doesn't work for pkg_resources.
diff --git a/docs/conf.py b/docs/conf.py
index 38a26e323fd..c1476acf177 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -125,7 +125,15 @@
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
-exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
+exclude_patterns = [
+ "_build",
+ "Thumbs.db",
+ ".DS_Store",
+ "examples/fork-process-model/flask-gunicorn",
+ "examples/fork-process-model/flask-uwsgi",
+ "examples/error_handler/error_handler_0",
+ "examples/error_handler/error_handler_1",
+]
autodoc_default_options = {
"members": True,
diff --git a/docs/examples/auto-instrumentation/README.rst b/docs/examples/auto-instrumentation/README.rst
index 9298c9bef2f..23fb47b3964 100644
--- a/docs/examples/auto-instrumentation/README.rst
+++ b/docs/examples/auto-instrumentation/README.rst
@@ -45,7 +45,7 @@ Manually instrumented server
return "served"
Server not instrumented manually
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``server_uninstrumented.py``
@@ -57,7 +57,7 @@ Server not instrumented manually
return "served"
Prepare
------------
+-------
Execute the following example in a separate virtual environment.
Run the following commands to prepare for auto-instrumentation:
@@ -69,7 +69,7 @@ Run the following commands to prepare for auto-instrumentation:
$ source auto_instrumentation/bin/activate
Install
-------------
+-------
Run the following commands to install the appropriate packages. The
``opentelemetry-instrumentation`` package provides several
@@ -90,7 +90,7 @@ a server as well as the process of executing an automatically
instrumented server.
Execute a manually instrumented server
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Execute the server in two separate consoles, one to run each of the
scripts that make up this example:
@@ -145,7 +145,7 @@ similar to the following example:
}
Execute an automatically instrumented server
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Stop the execution of ``server_instrumented.py`` with ``ctrl + c``
and run the following command instead:
@@ -208,7 +208,7 @@ You can see that both outputs are the same because automatic instrumentation doe
exactly what manual instrumentation does.
Instrumentation while debugging
-===============================
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The debug mode can be enabled in the Flask app like this:
@@ -226,3 +226,11 @@ reloader. To run instrumentation while the debug mode is enabled, set the
if __name__ == "__main__":
app.run(port=8082, debug=True, use_reloader=False)
+
+
+Additional resources
+~~~~~~~~~~~~~~~~~~~~
+
+In order to send telemetry to an OpenTelemetry Collector without doing any
+additional configuration, read about the `OpenTelemetry Distro <../distro/README.html>`_
+package.
diff --git a/docs/examples/basic_context/README.rst b/docs/examples/basic_context/README.rst
index 361192b96fc..1499a4bf8e6 100644
--- a/docs/examples/basic_context/README.rst
+++ b/docs/examples/basic_context/README.rst
@@ -1,14 +1,11 @@
Basic Context
=============
-These examples show how context is propagated through Spans in OpenTelemetry.
-
-There are three different examples:
+These examples show how context is propagated through Spans in OpenTelemetry. There are three different
+examples:
* implicit_context: Shows how starting a span implicitly creates context.
-
* child_context: Shows how context is propagated through child spans.
-
* async_context: Shows how context can be shared in another coroutine.
The source files of these examples are available :scm_web:`here `.
diff --git a/docs/examples/basic_tracer/README.rst b/docs/examples/basic_tracer/README.rst
index 618263b7a4f..572b4dc8704 100644
--- a/docs/examples/basic_tracer/README.rst
+++ b/docs/examples/basic_tracer/README.rst
@@ -1,12 +1,9 @@
Basic Trace
===========
-These examples show how to use OpenTelemetry to create and export Spans.
-
-There are two different examples:
+These examples show how to use OpenTelemetry to create and export Spans. There are two different examples:
* basic_trace: Shows how to configure a SpanProcessor and Exporter, and how to create a tracer and span.
-
* resources: Shows how to add resource information to a Provider.
The source files of these examples are available :scm_web:`here `.
diff --git a/docs/examples/datadog_exporter/README.rst b/docs/examples/datadog_exporter/README.rst
index 03f485da159..39e0798da30 100644
--- a/docs/examples/datadog_exporter/README.rst
+++ b/docs/examples/datadog_exporter/README.rst
@@ -1,5 +1,5 @@
-Datadog Exporter Example
-========================
+Datadog Exporter
+================
These examples show how to use OpenTelemetry to send tracing data to Datadog.
diff --git a/docs/examples/datadog_exporter/server.py b/docs/examples/datadog_exporter/server.py
index 662d3ebe97c..f032887a122 100644
--- a/docs/examples/datadog_exporter/server.py
+++ b/docs/examples/datadog_exporter/server.py
@@ -21,7 +21,7 @@
)
from opentelemetry.exporter.datadog.propagator import DatadogFormat
from opentelemetry.propagate import get_global_textmap, set_global_textmap
-from opentelemetry.propagators.composite import CompositeHTTPPropagator
+from opentelemetry.propagators.composite import CompositePropagator
from opentelemetry.sdk.trace import TracerProvider
app = Flask(__name__)
@@ -38,13 +38,11 @@
# append Datadog format for propagation to and from Datadog instrumented services
global_textmap = get_global_textmap()
-if isinstance(global_textmap, CompositeHTTPPropagator) and not any(
+if isinstance(global_textmap, CompositePropagator) and not any(
isinstance(p, DatadogFormat) for p in global_textmap._propagators
):
set_global_textmap(
- CompositeHTTPPropagator(
- global_textmap._propagators + [DatadogFormat()]
- )
+ CompositePropagator(global_textmap._propagators + [DatadogFormat()])
)
else:
set_global_textmap(DatadogFormat())
diff --git a/docs/examples/distro/README.rst b/docs/examples/distro/README.rst
new file mode 100644
index 00000000000..f58680609ab
--- /dev/null
+++ b/docs/examples/distro/README.rst
@@ -0,0 +1,104 @@
+OpenTelemetry Distro
+====================
+
+In order to make using OpenTelemetry and auto-instrumentation as quick as possible without sacrificing flexibility,
+OpenTelemetry distros provide a mechanism to automatically configure some of the more common options for users. By
+harnessing their power, users of OpenTelemetry can configure the components as they need. The ``opentelemetry-distro``
+package provides some defaults to users looking to get started, it configures:
+
+- the SDK TracerProvider
+- a BatchSpanProcessor
+- the OTLP ``SpanExporter`` to send data to an OpenTelemetry collector
+
+The package also provides a starting point for anyone interested in producing an alternative distro. The
+interfaces implemented by the package are loaded by the auto-instrumentation via the ``opentelemetry_distro``
+and ``opentelemetry_configurator`` entry points to configure the application before any other code is
+executed.
+
+In order to automatically export data from OpenTelemetry to the OpenTelemetry collector, installing the
+package will setup all the required entry points.
+
+.. code:: sh
+
+ $ pip install opentelemetry-distro[otlp] opentelemetry-instrumentation
+
+Start the Collector locally to see data being exported. Write the following file:
+
+.. code-block:: yaml
+
+ # /tmp/otel-collector-config.yaml
+ receivers:
+ otlp:
+ protocols:
+ grpc:
+ http:
+ exporters:
+ logging:
+ loglevel: debug
+ processors:
+ batch:
+ service:
+ pipelines:
+ traces:
+ receivers: [otlp]
+ exporters: [logging]
+ processors: [batch]
+
+Then start the Docker container:
+
+.. code-block:: sh
+
+ docker run -p 4317:4317 \
+ -v /tmp/otel-collector-config.yaml:/etc/otel-collector-config.yaml \
+ otel/opentelemetry-collector:latest \
+ --config=/etc/otel-collector-config.yaml
+
+The following code will create a span with no configuration.
+
+.. code:: python
+
+ # no_configuration.py
+ from opentelemetry import trace
+
+ with trace.get_tracer(__name__).start_as_current_span("foo"):
+ with trace.get_tracer(__name__).start_as_current_span("bar"):
+ print("baz")
+
+Lastly, run the ``no_configuration.py`` with the auto-instrumentation:
+
+.. code-block:: sh
+
+ $ opentelemetry-instrument python no_configuration.py
+
+The resulting span will appear in the output from the collector and look similar to this:
+
+.. code-block:: sh
+
+ Resource labels:
+ -> telemetry.sdk.language: STRING(python)
+ -> telemetry.sdk.name: STRING(opentelemetry)
+ -> telemetry.sdk.version: STRING(1.1.0)
+ -> service.name: STRING(unknown_service)
+ InstrumentationLibrarySpans #0
+ InstrumentationLibrary __main__
+ Span #0
+ Trace ID : db3c99e5bfc50ef8be1773c3765e8845
+ Parent ID : 0677126a4d110cb8
+ ID : 3163b3022808ed1b
+ Name : bar
+ Kind : SPAN_KIND_INTERNAL
+ Start time : 2021-05-06 22:54:51.23063 +0000 UTC
+ End time : 2021-05-06 22:54:51.230684 +0000 UTC
+ Status code : STATUS_CODE_UNSET
+ Status message :
+ Span #1
+ Trace ID : db3c99e5bfc50ef8be1773c3765e8845
+ Parent ID :
+ ID : 0677126a4d110cb8
+ Name : foo
+ Kind : SPAN_KIND_INTERNAL
+ Start time : 2021-05-06 22:54:51.230549 +0000 UTC
+ End time : 2021-05-06 22:54:51.230706 +0000 UTC
+ Status code : STATUS_CODE_UNSET
+ Status message :
+
diff --git a/docs/examples/django/README.rst b/docs/examples/django/README.rst
index 42316d01ee2..656a07b6da8 100644
--- a/docs/examples/django/README.rst
+++ b/docs/examples/django/README.rst
@@ -1,5 +1,5 @@
-OpenTelemetry Django Instrumentation Example
-============================================
+Django Instrumentation
+======================
This shows how to use ``opentelemetry-instrumentation-django`` to automatically instrument a
Django app.
@@ -142,4 +142,4 @@ References
* `Django `_
* `OpenTelemetry Project `_
-* `OpenTelemetry Django extension `_
+* `OpenTelemetry Django extension `_
diff --git a/docs/examples/opencensus-exporter-tracer/README.rst b/docs/examples/opencensus-exporter-tracer/README.rst
index d147f008d49..3047987c2c4 100644
--- a/docs/examples/opencensus-exporter-tracer/README.rst
+++ b/docs/examples/opencensus-exporter-tracer/README.rst
@@ -1,5 +1,5 @@
-OpenTelemetry Collector Tracer OpenCensus Exporter Example
-==========================================================
+OpenCensus Exporter
+===================
This example shows how to use the OpenCensus Exporter to export traces to the
OpenTelemetry collector.
diff --git a/docs/examples/opentracing/README.rst b/docs/examples/opentracing/README.rst
index da9c70e43b8..0bf5f8dca3d 100644
--- a/docs/examples/opentracing/README.rst
+++ b/docs/examples/opentracing/README.rst
@@ -1,5 +1,5 @@
-OpenTracing Shim Example
-==========================
+OpenTracing Shim
+================
This example shows how to use the :doc:`opentelemetry-opentracing-shim
package <../../shim/opentracing_shim/opentracing_shim>`
diff --git a/docs/exporter/jaeger/jaeger.rst b/docs/exporter/jaeger/jaeger.rst
index 5a49f72b5c8..1fc02948d8d 100644
--- a/docs/exporter/jaeger/jaeger.rst
+++ b/docs/exporter/jaeger/jaeger.rst
@@ -1,4 +1,4 @@
-Opentelemetry Jaeger Exporters
+OpenTelemetry Jaeger Exporters
==============================
.. automodule:: opentelemetry.exporter.jaeger
diff --git a/docs/exporter/otlp/otlp.rst b/docs/exporter/otlp/otlp.rst
index e4bfdd07a1d..471f2935fb7 100644
--- a/docs/exporter/otlp/otlp.rst
+++ b/docs/exporter/otlp/otlp.rst
@@ -1,4 +1,4 @@
-Opentelemetry OTLP Exporters
+OpenTelemetry OTLP Exporters
============================
.. automodule:: opentelemetry.exporter.otlp
diff --git a/docs/getting_started/otlpcollector_example.py b/docs/getting_started/otlpcollector_example.py
index 48c0d32a594..71f9ed97541 100644
--- a/docs/getting_started/otlpcollector_example.py
+++ b/docs/getting_started/otlpcollector_example.py
@@ -24,7 +24,7 @@
span_exporter = OTLPSpanExporter(
# optional
- # endpoint:="myCollectorURL:4317",
+ # endpoint="myCollectorURL:4317",
# credentials=ChannelCredentials(credentials),
# headers=(("metadata", "metadata")),
)
diff --git a/docs/index.rst b/docs/index.rst
index 42969c058f5..fe1b2a85388 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -72,7 +72,7 @@ install
.. toctree::
:maxdepth: 1
- :caption: OpenTelemetry Python Packages
+ :caption: Core Packages
:name: packages
api/api
@@ -80,7 +80,7 @@ install
.. toctree::
:maxdepth: 2
- :caption: OpenTelemetry Exporters
+ :caption: Exporters
:name: exporters
:glob:
@@ -88,7 +88,7 @@ install
.. toctree::
:maxdepth: 2
- :caption: OpenTelemetry Shims
+ :caption: Shims
:name: Shims
:glob:
@@ -96,7 +96,7 @@ install
.. toctree::
:maxdepth: 1
- :caption: OpenTelemetry Python Performance
+ :caption: Performance
:name: performance-tests
:glob:
diff --git a/exporter/opentelemetry-exporter-jaeger-proto-grpc/src/opentelemetry/exporter/jaeger/proto/grpc/translate/__init__.py b/exporter/opentelemetry-exporter-jaeger-proto-grpc/src/opentelemetry/exporter/jaeger/proto/grpc/translate/__init__.py
index 778296a917c..e644e213603 100644
--- a/exporter/opentelemetry-exporter-jaeger-proto-grpc/src/opentelemetry/exporter/jaeger/proto/grpc/translate/__init__.py
+++ b/exporter/opentelemetry-exporter-jaeger-proto-grpc/src/opentelemetry/exporter/jaeger/proto/grpc/translate/__init__.py
@@ -318,10 +318,18 @@ def _extract_tags(
def _extract_refs(
self, span: ReadableSpan
) -> Optional[Sequence[model_pb2.SpanRef]]:
- if not span.links:
- return None
refs = []
+ if span.parent:
+ ctx = span.get_span_context()
+ parent_id = span.parent.span_id
+ parent_ref = model_pb2.SpanRef(
+ ref_type=model_pb2.SpanRefType.CHILD_OF,
+ trace_id=_trace_id_to_bytes(ctx.trace_id),
+ span_id=_span_id_to_bytes(parent_id),
+ )
+ refs.append(parent_ref)
+
for link in span.links:
trace_id = link.context.trace_id
span_id = link.context.span_id
diff --git a/exporter/opentelemetry-exporter-jaeger-proto-grpc/tests/test_jaeger_exporter_protobuf.py b/exporter/opentelemetry-exporter-jaeger-proto-grpc/tests/test_jaeger_exporter_protobuf.py
index 99b3f093cb8..383e33d0f16 100644
--- a/exporter/opentelemetry-exporter-jaeger-proto-grpc/tests/test_jaeger_exporter_protobuf.py
+++ b/exporter/opentelemetry-exporter-jaeger-proto-grpc/tests/test_jaeger_exporter_protobuf.py
@@ -287,11 +287,16 @@ def test_translate_to_jaeger(self):
),
],
references=[
+ model_pb2.SpanRef(
+ ref_type=model_pb2.SpanRefType.CHILD_OF,
+ trace_id=pb_translator._trace_id_to_bytes(trace_id),
+ span_id=pb_translator._span_id_to_bytes(parent_id),
+ ),
model_pb2.SpanRef(
ref_type=model_pb2.SpanRefType.FOLLOWS_FROM,
trace_id=pb_translator._trace_id_to_bytes(trace_id),
span_id=pb_translator._span_id_to_bytes(other_id),
- )
+ ),
],
logs=[
model_pb2.Log(
diff --git a/opentelemetry-api/setup.cfg b/opentelemetry-api/setup.cfg
index e712564260f..99d52838fb1 100644
--- a/opentelemetry-api/setup.cfg
+++ b/opentelemetry-api/setup.cfg
@@ -42,6 +42,7 @@ packages=find_namespace:
zip_safe = False
include_package_data = True
install_requires =
+ Deprecated >= 1.2.6
aiocontextvars; python_version<'3.7'
[options.packages.find]
diff --git a/opentelemetry-api/src/opentelemetry/propagate/__init__.py b/opentelemetry-api/src/opentelemetry/propagate/__init__.py
index 6c63edec3cb..3ad1ad544ab 100644
--- a/opentelemetry-api/src/opentelemetry/propagate/__init__.py
+++ b/opentelemetry-api/src/opentelemetry/propagate/__init__.py
@@ -16,13 +16,13 @@
API for propagation of context.
The propagators for the
-``opentelemetry.propagators.composite.CompositeHTTPPropagator`` can be defined
+``opentelemetry.propagators.composite.CompositePropagator`` can be defined
via configuration in the ``OTEL_PROPAGATORS`` environment variable. This
variable should be set to a comma-separated string of names of values for the
``opentelemetry_propagator`` entry point. For example, setting
``OTEL_PROPAGATORS`` to ``tracecontext,baggage`` (which is the default value)
would instantiate
-``opentelemetry.propagators.composite.CompositeHTTPPropagator`` with 2
+``opentelemetry.propagators.composite.CompositePropagator`` with 2
propagators, one of type
``opentelemetry.trace.propagation.tracecontext.TraceContextTextMapPropagator``
and other of type ``opentelemetry.baggage.propagation.W3CBaggagePropagator``.
@@ -96,7 +96,7 @@ def extract(
used to construct a Context. This object
must be paired with an appropriate getter
which understands how to extract a value from it.
- context: an optional Context to use. Defaults to current
+ context: an optional Context to use. Defaults to root
context if not set.
"""
return get_global_textmap().extract(carrier, context, getter=getter)
@@ -142,7 +142,7 @@ def inject(
logger.exception("Failed to load configured propagators")
raise
-_HTTP_TEXT_FORMAT = composite.CompositeHTTPPropagator(propagators) # type: ignore
+_HTTP_TEXT_FORMAT = composite.CompositePropagator(propagators) # type: ignore
def get_global_textmap() -> textmap.TextMapPropagator:
diff --git a/opentelemetry-api/src/opentelemetry/propagators/composite.py b/opentelemetry-api/src/opentelemetry/propagators/composite.py
index c027f638dcd..b06e385b588 100644
--- a/opentelemetry-api/src/opentelemetry/propagators/composite.py
+++ b/opentelemetry-api/src/opentelemetry/propagators/composite.py
@@ -14,14 +14,16 @@
import logging
import typing
+from deprecated import deprecated
+
from opentelemetry.context.context import Context
from opentelemetry.propagators import textmap
logger = logging.getLogger(__name__)
-class CompositeHTTPPropagator(textmap.TextMapPropagator):
- """CompositeHTTPPropagator provides a mechanism for combining multiple
+class CompositePropagator(textmap.TextMapPropagator):
+ """CompositePropagator provides a mechanism for combining multiple
propagators into a single one.
Args:
@@ -80,3 +82,10 @@ def fields(self) -> typing.Set[str]:
composite_fields.add(field)
return composite_fields
+
+
+@deprecated(version="1.2.0", reason="You should use CompositePropagator") # type: ignore
+class CompositeHTTPPropagator(CompositePropagator):
+ """CompositeHTTPPropagator provides a mechanism for combining multiple
+ propagators into a single one.
+ """
diff --git a/opentelemetry-api/src/opentelemetry/propagators/textmap.py b/opentelemetry-api/src/opentelemetry/propagators/textmap.py
index 45c2308f661..0011315cf21 100644
--- a/opentelemetry-api/src/opentelemetry/propagators/textmap.py
+++ b/opentelemetry-api/src/opentelemetry/propagators/textmap.py
@@ -150,7 +150,7 @@ def extract(
used to construct a Context. This object
must be paired with an appropriate getter
which understands how to extract a value from it.
- context: an optional Context to use. Defaults to current
+ context: an optional Context to use. Defaults to root
context if not set.
Returns:
A Context with configuration found in the carrier.
diff --git a/opentelemetry-api/src/opentelemetry/trace/propagation/tracecontext.py b/opentelemetry-api/src/opentelemetry/trace/propagation/tracecontext.py
index 9fc5cfed242..001db5c7293 100644
--- a/opentelemetry-api/src/opentelemetry/trace/propagation/tracecontext.py
+++ b/opentelemetry-api/src/opentelemetry/trace/propagation/tracecontext.py
@@ -43,14 +43,17 @@ def extract(
See `opentelemetry.propagators.textmap.TextMapPropagator.extract`
"""
+ if context is None:
+ context = Context()
+
header = getter.get(carrier, self._TRACEPARENT_HEADER_NAME)
if not header:
- return trace.set_span_in_context(trace.INVALID_SPAN, context)
+ return context
match = re.search(self._TRACEPARENT_HEADER_FORMAT_RE, header[0])
if not match:
- return trace.set_span_in_context(trace.INVALID_SPAN, context)
+ return context
version = match.group(1)
trace_id = match.group(2)
@@ -58,13 +61,13 @@ def extract(
trace_flags = match.group(4)
if trace_id == "0" * 32 or span_id == "0" * 16:
- return trace.set_span_in_context(trace.INVALID_SPAN, context)
+ return context
if version == "00":
if match.group(5):
- return trace.set_span_in_context(trace.INVALID_SPAN, context)
+ return context
if version == "ff":
- return trace.set_span_in_context(trace.INVALID_SPAN, context)
+ return context
tracestate_headers = getter.get(carrier, self._TRACESTATE_HEADER_NAME)
if tracestate_headers is None:
diff --git a/opentelemetry-api/src/opentelemetry/util/_time.py b/opentelemetry-api/src/opentelemetry/util/_time.py
index aa61bc02aa4..0099d3dbb5c 100644
--- a/opentelemetry-api/src/opentelemetry/util/_time.py
+++ b/opentelemetry-api/src/opentelemetry/util/_time.py
@@ -18,7 +18,7 @@
if version_info.minor < 7:
getLogger(__name__).warning( # pylint: disable=logging-not-lazy
"You are using Python 3.%s. This version does not support timestamps "
- "with nanosecond precision and the Opentelemetry SDK will use "
+ "with nanosecond precision and the OpenTelemetry SDK will use "
"millisecond precision instead. Please refer to PEP 546 for more "
"information. Please upgrade to Python 3.7 or newer to use nanosecond "
"precision." % version_info.minor
diff --git a/opentelemetry-api/tests/propagators/test_composite.py b/opentelemetry-api/tests/propagators/test_composite.py
index ef9fae2a1ac..d6bf6960cdf 100644
--- a/opentelemetry-api/tests/propagators/test_composite.py
+++ b/opentelemetry-api/tests/propagators/test_composite.py
@@ -17,7 +17,7 @@
import unittest
from unittest.mock import Mock
-from opentelemetry.propagators.composite import CompositeHTTPPropagator
+from opentelemetry.propagators.composite import CompositePropagator
def get_as_list(dict_object, key):
@@ -67,7 +67,7 @@ def setUpClass(cls):
)
def test_no_propagators(self):
- propagator = CompositeHTTPPropagator([])
+ propagator = CompositePropagator([])
new_carrier = {}
propagator.inject(new_carrier)
self.assertEqual(new_carrier, {})
@@ -78,7 +78,7 @@ def test_no_propagators(self):
self.assertEqual(context, {})
def test_single_propagator(self):
- propagator = CompositeHTTPPropagator([self.mock_propagator_0])
+ propagator = CompositePropagator([self.mock_propagator_0])
new_carrier = {}
propagator.inject(new_carrier)
@@ -90,7 +90,7 @@ def test_single_propagator(self):
self.assertEqual(context, {"mock-0": "context"})
def test_multiple_propagators(self):
- propagator = CompositeHTTPPropagator(
+ propagator = CompositePropagator(
[self.mock_propagator_0, self.mock_propagator_1]
)
@@ -106,7 +106,7 @@ def test_multiple_propagators(self):
def test_multiple_propagators_same_key(self):
# test that when multiple propagators extract/inject the same
# key, the later propagator values are extracted/injected
- propagator = CompositeHTTPPropagator(
+ propagator = CompositePropagator(
[self.mock_propagator_0, self.mock_propagator_2]
)
@@ -120,7 +120,7 @@ def test_multiple_propagators_same_key(self):
self.assertEqual(context, {"mock-0": "context2"})
def test_fields(self):
- propagator = CompositeHTTPPropagator(
+ propagator = CompositePropagator(
[
self.mock_propagator_0,
self.mock_propagator_1,
diff --git a/opentelemetry-api/tests/propagators/test_propagators.py b/opentelemetry-api/tests/propagators/test_propagators.py
index 80299d34904..bcc01094656 100644
--- a/opentelemetry-api/tests/propagators/test_propagators.py
+++ b/opentelemetry-api/tests/propagators/test_propagators.py
@@ -27,7 +27,7 @@
class TestPropagators(TestCase):
- @patch("opentelemetry.propagators.composite.CompositeHTTPPropagator")
+ @patch("opentelemetry.propagators.composite.CompositePropagator")
def test_default_composite_propagators(self, mock_compositehttppropagator):
def test_propagators(propagators):
@@ -48,7 +48,7 @@ def test_propagators(propagators):
reload(opentelemetry.propagate)
@patch.dict(environ, {OTEL_PROPAGATORS: "a,b,c"})
- @patch("opentelemetry.propagators.composite.CompositeHTTPPropagator")
+ @patch("opentelemetry.propagators.composite.CompositePropagator")
@patch("pkg_resources.iter_entry_points")
def test_non_default_propagators(
self, mock_iter_entry_points, mock_compositehttppropagator
diff --git a/opentelemetry-api/tests/trace/propagation/test_tracecontexthttptextformat.py b/opentelemetry-api/tests/trace/propagation/test_tracecontexthttptextformat.py
index 98ca50610b9..9d9561a4a5e 100644
--- a/opentelemetry-api/tests/trace/propagation/test_tracecontexthttptextformat.py
+++ b/opentelemetry-api/tests/trace/propagation/test_tracecontexthttptextformat.py
@@ -19,6 +19,7 @@
from unittest.mock import Mock, patch
from opentelemetry import trace
+from opentelemetry.context import Context
from opentelemetry.trace.propagation import tracecontext
from opentelemetry.trace.span import TraceState
@@ -270,3 +271,51 @@ def test_fields(self, mock_get_current_span, mock_invalid_span_context):
inject_fields.add(mock_call[1][1])
self.assertEqual(inject_fields, FORMAT.fields)
+
+ def test_extract_no_trace_parent_to_explicit_ctx(self):
+ carrier = {"tracestate": ["foo=1"]}
+ orig_ctx = Context({"k1": "v1"})
+
+ ctx = FORMAT.extract(carrier, orig_ctx)
+ self.assertDictEqual(orig_ctx, ctx)
+
+ def test_extract_no_trace_parent_to_implicit_ctx(self):
+ carrier = {"tracestate": ["foo=1"]}
+
+ ctx = FORMAT.extract(carrier)
+ self.assertDictEqual(Context(), ctx)
+
+ def test_extract_invalid_trace_parent_to_explicit_ctx(self):
+ trace_parent_headers = [
+ "invalid",
+ "00-00000000000000000000000000000000-1234567890123456-00",
+ "00-12345678901234567890123456789012-0000000000000000-00",
+ "00-12345678901234567890123456789012-1234567890123456-00-residue",
+ ]
+ for trace_parent in trace_parent_headers:
+ with self.subTest(trace_parent=trace_parent):
+ carrier = {
+ "traceparent": [trace_parent],
+ "tracestate": ["foo=1"],
+ }
+ orig_ctx = Context({"k1": "v1"})
+
+ ctx = FORMAT.extract(carrier, orig_ctx)
+ self.assertDictEqual(orig_ctx, ctx)
+
+ def test_extract_invalid_trace_parent_to_implicit_ctx(self):
+ trace_parent_headers = [
+ "invalid",
+ "00-00000000000000000000000000000000-1234567890123456-00",
+ "00-12345678901234567890123456789012-0000000000000000-00",
+ "00-12345678901234567890123456789012-1234567890123456-00-residue",
+ ]
+ for trace_parent in trace_parent_headers:
+ with self.subTest(trace_parent=trace_parent):
+ carrier = {
+ "traceparent": [trace_parent],
+ "tracestate": ["foo=1"],
+ }
+
+ ctx = FORMAT.extract(carrier)
+ self.assertDictEqual(Context(), ctx)
diff --git a/opentelemetry-distro/README.rst b/opentelemetry-distro/README.rst
index 4189131fc26..80952839104 100644
--- a/opentelemetry-distro/README.rst
+++ b/opentelemetry-distro/README.rst
@@ -14,9 +14,10 @@ Installation
pip install opentelemetry-distro
-This package provides entrypoints to configure OpenTelemetry
+This package provides entrypoints to configure OpenTelemetry.
References
----------
* `OpenTelemetry Project `_
+* `Example using opentelemetry-distro `_
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/environment_variables/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/environment_variables/__init__.py
index 9289d1c44ad..0aa5aa515d7 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/environment_variables/__init__.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/environment_variables/__init__.py
@@ -218,3 +218,20 @@
"""
.. envvar:: OTEL_EXPORTER_JAEGER_AGENT_SPLIT_OVERSIZED_BATCHES
"""
+
+OTEL_SERVICE_NAME = "OTEL_SERVICE_NAME"
+"""
+.. envvar:: OTEL_SERVICE_NAME
+
+Convenience environment variable for setting the service name resource attribute.
+The following two environment variables have the same effect
+
+.. code-block:: console
+
+ OTEL_SERVICE_NAME=my-python-service
+
+ OTEL_RESOURCE_ATTRIBUTES=service.name=my-python-service
+
+
+If both are set, :envvar:`OTEL_SERVICE_NAME` takes precedence.
+"""
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py
index 6cffff42639..77240b8ef8e 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py
@@ -64,7 +64,10 @@
import pkg_resources
-from opentelemetry.sdk.environment_variables import OTEL_RESOURCE_ATTRIBUTES
+from opentelemetry.sdk.environment_variables import (
+ OTEL_RESOURCE_ATTRIBUTES,
+ OTEL_SERVICE_NAME,
+)
from opentelemetry.semconv.resource import ResourceAttributes
LabelValue = typing.Union[str, bool, int, float]
@@ -231,6 +234,9 @@ def detect(self) -> "Resource":
item.split("=") for item in env_resources_items.split(",")
)
}
+ service_name = os.environ.get(OTEL_SERVICE_NAME)
+ if service_name:
+ env_resource_map[SERVICE_NAME] = service_name
return Resource(env_resource_map)
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py
index 625b8519ef5..ece7da6d225 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py
@@ -483,9 +483,13 @@ def to_json(self, indent=4):
if self.parent is not None:
if isinstance(self.parent, Span):
ctx = self.parent.context
- parent_id = trace_api.format_span_id(ctx.span_id)
+ parent_id = "0x{}".format(
+ trace_api.format_span_id(ctx.span_id)
+ )
elif isinstance(self.parent, SpanContext):
- parent_id = trace_api.format_span_id(self.parent.span_id)
+ parent_id = "0x{}".format(
+ trace_api.format_span_id(self.parent.span_id)
+ )
start_time = None
if self._start_time:
@@ -685,7 +689,10 @@ def set_attributes(
try:
value = value.decode()
except ValueError:
- logger.warning("Byte attribute could not be decoded.")
+ logger.warning(
+ "Byte attribute could not be decoded for key `%s`.",
+ key,
+ )
return
self._attributes[key] = value
diff --git a/opentelemetry-sdk/tests/resources/test_resources.py b/opentelemetry-sdk/tests/resources/test_resources.py
index 4b8fd71da27..211187f1ebb 100644
--- a/opentelemetry-sdk/tests/resources/test_resources.py
+++ b/opentelemetry-sdk/tests/resources/test_resources.py
@@ -232,6 +232,20 @@ def test_env_priority(self):
self.assertEqual(resource_env_override.attributes["key1"], "value1")
self.assertEqual(resource_env_override.attributes["key2"], "value2")
+ @mock.patch.dict(
+ os.environ,
+ {
+ resources.OTEL_SERVICE_NAME: "test-srv-name",
+ resources.OTEL_RESOURCE_ATTRIBUTES: "service.name=svc-name-from-resource",
+ },
+ )
+ def test_service_name_env(self):
+ resource = resources.Resource.create()
+ self.assertEqual(resource.attributes["service.name"], "test-srv-name")
+
+ resource = resources.Resource.create({"service.name": "from-code"})
+ self.assertEqual(resource.attributes["service.name"], "from-code")
+
class TestOTELResourceDetector(unittest.TestCase):
def setUp(self) -> None:
@@ -270,3 +284,28 @@ def test_multiple_with_whitespace(self):
self.assertEqual(
detector.detect(), resources.Resource({"k": "v", "k2": "v2"})
)
+
+ @mock.patch.dict(
+ os.environ,
+ {resources.OTEL_SERVICE_NAME: "test-srv-name"},
+ )
+ def test_service_name_env(self):
+ detector = resources.OTELResourceDetector()
+ self.assertEqual(
+ detector.detect(),
+ resources.Resource({"service.name": "test-srv-name"}),
+ )
+
+ @mock.patch.dict(
+ os.environ,
+ {
+ resources.OTEL_SERVICE_NAME: "from-service-name",
+ resources.OTEL_RESOURCE_ATTRIBUTES: "service.name=from-resource-attrs",
+ },
+ )
+ def test_service_name_env_precedence(self):
+ detector = resources.OTELResourceDetector()
+ self.assertEqual(
+ detector.detect(),
+ resources.Resource({"service.name": "from-service-name"}),
+ )
diff --git a/opentelemetry-sdk/tests/trace/test_trace.py b/opentelemetry-sdk/tests/trace/test_trace.py
index df2454a5215..f12dc7c75cf 100644
--- a/opentelemetry-sdk/tests/trace/test_trace.py
+++ b/opentelemetry-sdk/tests/trace/test_trace.py
@@ -1221,7 +1221,10 @@ def test_to_json(self):
is_remote=False,
trace_flags=trace_api.TraceFlags(trace_api.TraceFlags.SAMPLED),
)
- span = trace._Span("span-name", context, resource=Resource({}))
+ parent = trace._Span("parent-name", context, resource=Resource({}))
+ span = trace._Span(
+ "span-name", context, resource=Resource({}), parent=parent
+ )
self.assertEqual(
span.to_json(),
@@ -1233,7 +1236,7 @@ def test_to_json(self):
"trace_state": "[]"
},
"kind": "SpanKind.INTERNAL",
- "parent_id": null,
+ "parent_id": "0x00000000deadbef0",
"start_time": null,
"end_time": null,
"status": {
@@ -1247,7 +1250,7 @@ def test_to_json(self):
)
self.assertEqual(
span.to_json(indent=None),
- '{"name": "span-name", "context": {"trace_id": "0x000000000000000000000000deadbeef", "span_id": "0x00000000deadbef0", "trace_state": "[]"}, "kind": "SpanKind.INTERNAL", "parent_id": null, "start_time": null, "end_time": null, "status": {"status_code": "UNSET"}, "attributes": {}, "events": [], "links": [], "resource": {}}',
+ '{"name": "span-name", "context": {"trace_id": "0x000000000000000000000000deadbeef", "span_id": "0x00000000deadbef0", "trace_state": "[]"}, "kind": "SpanKind.INTERNAL", "parent_id": "0x00000000deadbef0", "start_time": null, "end_time": null, "status": {"status_code": "UNSET"}, "attributes": {}, "events": [], "links": [], "resource": {}}',
)
def test_attributes_to_json(self):
diff --git a/propagator/opentelemetry-propagator-b3/src/opentelemetry/propagators/b3/__init__.py b/propagator/opentelemetry-propagator-b3/src/opentelemetry/propagators/b3/__init__.py
index 6977bc32c64..d0beec401a8 100644
--- a/propagator/opentelemetry-propagator-b3/src/opentelemetry/propagators/b3/__init__.py
+++ b/propagator/opentelemetry-propagator-b3/src/opentelemetry/propagators/b3/__init__.py
@@ -50,6 +50,8 @@ def extract(
context: typing.Optional[Context] = None,
getter: Getter = default_getter,
) -> Context:
+ if context is None:
+ context = Context()
trace_id = trace.INVALID_TRACE_ID
span_id = trace.INVALID_SPAN_ID
sampled = "0"
@@ -97,8 +99,6 @@ def extract(
or self._trace_id_regex.fullmatch(trace_id) is None
or self._span_id_regex.fullmatch(span_id) is None
):
- if context is None:
- return trace.set_span_in_context(trace.INVALID_SPAN, context)
return context
trace_id = int(trace_id, 16)
diff --git a/propagator/opentelemetry-propagator-b3/tests/test_b3_format.py b/propagator/opentelemetry-propagator-b3/tests/test_b3_format.py
index 6ee0be2ce1c..29d8a472ea3 100644
--- a/propagator/opentelemetry-propagator-b3/tests/test_b3_format.py
+++ b/propagator/opentelemetry-propagator-b3/tests/test_b3_format.py
@@ -19,7 +19,7 @@
import opentelemetry.sdk.trace as trace
import opentelemetry.sdk.trace.id_generator as id_generator
import opentelemetry.trace as trace_api
-from opentelemetry.context import get_current
+from opentelemetry.context import Context, get_current
from opentelemetry.propagators.textmap import DefaultGetter
FORMAT = b3_format.B3Format()
@@ -219,7 +219,7 @@ def test_flags_and_sampling(self):
def test_derived_ctx_is_returned_for_success(self):
"""Ensure returned context is derived from the given context."""
- old_ctx = {"k1": "v1"}
+ old_ctx = Context({"k1": "v1"})
new_ctx = FORMAT.extract(
{
FORMAT.TRACE_ID_KEY: self.serialized_trace_id,
@@ -229,17 +229,19 @@ def test_derived_ctx_is_returned_for_success(self):
old_ctx,
)
self.assertIn("current-span", new_ctx)
- for key, value in old_ctx.items():
+ for key, value in old_ctx.items(): # pylint:disable=no-member
self.assertIn(key, new_ctx)
+ # pylint:disable=unsubscriptable-object
self.assertEqual(new_ctx[key], value)
def test_derived_ctx_is_returned_for_failure(self):
"""Ensure returned context is derived from the given context."""
- old_ctx = {"k2": "v2"}
+ old_ctx = Context({"k2": "v2"})
new_ctx = FORMAT.extract({}, old_ctx)
self.assertNotIn("current-span", new_ctx)
- for key, value in old_ctx.items():
+ for key, value in old_ctx.items(): # pylint:disable=no-member
self.assertIn(key, new_ctx)
+ # pylint:disable=unsubscriptable-object
self.assertEqual(new_ctx[key], value)
def test_64bit_trace_id(self):
@@ -258,18 +260,24 @@ def test_64bit_trace_id(self):
new_carrier[FORMAT.TRACE_ID_KEY], "0" * 16 + trace_id_64_bit
)
- def test_extract_invalid_single_header(self):
+ def test_extract_invalid_single_header_to_explicit_ctx(self):
"""Given unparsable header, do not modify context"""
- old_ctx = {}
+ old_ctx = Context({"k1": "v1"})
carrier = {FORMAT.SINGLE_HEADER_KEY: "0-1-2-3-4-5-6-7"}
new_ctx = FORMAT.extract(carrier, old_ctx)
self.assertDictEqual(new_ctx, old_ctx)
- def test_extract_missing_trace_id(self):
+ def test_extract_invalid_single_header_to_implicit_ctx(self):
+ carrier = {FORMAT.SINGLE_HEADER_KEY: "0-1-2-3-4-5-6-7"}
+ new_ctx = FORMAT.extract(carrier)
+
+ self.assertDictEqual(Context(), new_ctx)
+
+ def test_extract_missing_trace_id_to_explicit_ctx(self):
"""Given no trace ID, do not modify context"""
- old_ctx = {}
+ old_ctx = Context({"k1": "v1"})
carrier = {
FORMAT.SPAN_ID_KEY: self.serialized_span_id,
@@ -279,9 +287,18 @@ def test_extract_missing_trace_id(self):
self.assertDictEqual(new_ctx, old_ctx)
- def test_extract_invalid_trace_id(self):
+ def test_extract_missing_trace_id_to_implicit_ctx(self):
+ carrier = {
+ FORMAT.SPAN_ID_KEY: self.serialized_span_id,
+ FORMAT.FLAGS_KEY: "1",
+ }
+ new_ctx = FORMAT.extract(carrier)
+
+ self.assertDictEqual(Context(), new_ctx)
+
+ def test_extract_invalid_trace_id_to_explicit_ctx(self):
"""Given invalid trace ID, do not modify context"""
- old_ctx = {}
+ old_ctx = Context({"k1": "v1"})
carrier = {
FORMAT.TRACE_ID_KEY: "abc123",
@@ -292,9 +309,19 @@ def test_extract_invalid_trace_id(self):
self.assertDictEqual(new_ctx, old_ctx)
- def test_extract_invalid_span_id(self):
+ def test_extract_invalid_trace_id_to_implicit_ctx(self):
+ carrier = {
+ FORMAT.TRACE_ID_KEY: "abc123",
+ FORMAT.SPAN_ID_KEY: self.serialized_span_id,
+ FORMAT.FLAGS_KEY: "1",
+ }
+ new_ctx = FORMAT.extract(carrier)
+
+ self.assertDictEqual(Context(), new_ctx)
+
+ def test_extract_invalid_span_id_to_explicit_ctx(self):
"""Given invalid span ID, do not modify context"""
- old_ctx = {}
+ old_ctx = Context({"k1": "v1"})
carrier = {
FORMAT.TRACE_ID_KEY: self.serialized_trace_id,
@@ -305,9 +332,19 @@ def test_extract_invalid_span_id(self):
self.assertDictEqual(new_ctx, old_ctx)
- def test_extract_missing_span_id(self):
+ def test_extract_invalid_span_id_to_implicit_ctx(self):
+ carrier = {
+ FORMAT.TRACE_ID_KEY: self.serialized_trace_id,
+ FORMAT.SPAN_ID_KEY: "abc123",
+ FORMAT.FLAGS_KEY: "1",
+ }
+ new_ctx = FORMAT.extract(carrier)
+
+ self.assertDictEqual(Context(), new_ctx)
+
+ def test_extract_missing_span_id_to_explicit_ctx(self):
"""Given no span ID, do not modify context"""
- old_ctx = {}
+ old_ctx = Context({"k1": "v1"})
carrier = {
FORMAT.TRACE_ID_KEY: self.serialized_trace_id,
@@ -317,15 +354,28 @@ def test_extract_missing_span_id(self):
self.assertDictEqual(new_ctx, old_ctx)
- def test_extract_empty_carrier(self):
+ def test_extract_missing_span_id_to_implicit_ctx(self):
+ carrier = {
+ FORMAT.TRACE_ID_KEY: self.serialized_trace_id,
+ FORMAT.FLAGS_KEY: "1",
+ }
+ new_ctx = FORMAT.extract(carrier)
+
+ self.assertDictEqual(Context(), new_ctx)
+
+ def test_extract_empty_carrier_to_explicit_ctx(self):
"""Given no headers at all, do not modify context"""
- old_ctx = {}
+ old_ctx = Context({"k1": "v1"})
carrier = {}
new_ctx = FORMAT.extract(carrier, old_ctx)
self.assertDictEqual(new_ctx, old_ctx)
+ def test_extract_empty_carrier_to_implicit_ctx(self):
+ new_ctx = FORMAT.extract({})
+ self.assertDictEqual(Context(), new_ctx)
+
@staticmethod
def test_inject_empty_context():
"""If the current context has no span, don't add headers"""
@@ -368,5 +418,4 @@ def test_extract_none_context(self):
carrier = {}
new_ctx = FORMAT.extract(carrier, old_ctx)
- self.assertIsNotNone(new_ctx)
- self.assertEqual(new_ctx["current-span"], trace_api.INVALID_SPAN)
+ self.assertDictEqual(Context(), new_ctx)
diff --git a/propagator/opentelemetry-propagator-jaeger/src/opentelemetry/propagators/jaeger/__init__.py b/propagator/opentelemetry-propagator-jaeger/src/opentelemetry/propagators/jaeger/__init__.py
index 47f438531fb..974b9143a5a 100644
--- a/propagator/opentelemetry-propagator-jaeger/src/opentelemetry/propagators/jaeger/__init__.py
+++ b/propagator/opentelemetry-propagator-jaeger/src/opentelemetry/propagators/jaeger/__init__.py
@@ -47,31 +47,26 @@ def extract(
) -> Context:
if context is None:
- context = get_current()
+ context = Context()
header = getter.get(carrier, self.TRACE_ID_KEY)
if not header:
- return trace.set_span_in_context(trace.INVALID_SPAN, context)
- fields = _extract_first_element(header).split(":")
+ return context
context = self._extract_baggage(getter, carrier, context)
- if len(fields) != 4:
- return trace.set_span_in_context(trace.INVALID_SPAN, context)
- trace_id, span_id, _parent_id, flags = fields
+ trace_id, span_id, flags = _parse_trace_id_header(header)
if (
trace_id == trace.INVALID_TRACE_ID
or span_id == trace.INVALID_SPAN_ID
):
- return trace.set_span_in_context(trace.INVALID_SPAN, context)
+ return context
span = trace.NonRecordingSpan(
trace.SpanContext(
- trace_id=int(trace_id, 16),
- span_id=int(span_id, 16),
+ trace_id=trace_id,
+ span_id=span_id,
is_remote=True,
- trace_flags=trace.TraceFlags(
- int(flags, 16) & trace.TraceFlags.SAMPLED
- ),
+ trace_flags=trace.TraceFlags(flags & trace.TraceFlags.SAMPLED),
)
)
return trace.set_span_in_context(span, context)
@@ -147,3 +142,35 @@ def _extract_first_element(
if items is None:
return None
return next(iter(items), None)
+
+
+def _parse_trace_id_header(
+ items: typing.Iterable[CarrierT],
+) -> typing.Tuple[int]:
+ invalid_header_result = (trace.INVALID_TRACE_ID, trace.INVALID_SPAN_ID, 0)
+
+ header = _extract_first_element(items)
+ if header is None:
+ return invalid_header_result
+
+ fields = header.split(":")
+ if len(fields) != 4:
+ return invalid_header_result
+
+ trace_id_str, span_id_str, _parent_id_str, flags_str = fields
+ flags = _int_from_hex_str(flags_str, None)
+ if flags is None:
+ return invalid_header_result
+
+ trace_id = _int_from_hex_str(trace_id_str, trace.INVALID_TRACE_ID)
+ span_id = _int_from_hex_str(span_id_str, trace.INVALID_SPAN_ID)
+ return trace_id, span_id, flags
+
+
+def _int_from_hex_str(
+ identifier: str, default: typing.Optional[int]
+) -> typing.Optional[int]:
+ try:
+ return int(identifier, 16)
+ except ValueError:
+ return default
diff --git a/propagator/opentelemetry-propagator-jaeger/tests/test_jaeger_propagator.py b/propagator/opentelemetry-propagator-jaeger/tests/test_jaeger_propagator.py
index 12a0a028ddf..55e096b0954 100644
--- a/propagator/opentelemetry-propagator-jaeger/tests/test_jaeger_propagator.py
+++ b/propagator/opentelemetry-propagator-jaeger/tests/test_jaeger_propagator.py
@@ -19,6 +19,7 @@
import opentelemetry.sdk.trace.id_generator as id_generator
import opentelemetry.trace as trace_api
from opentelemetry import baggage
+from opentelemetry.context import Context
from opentelemetry.propagators import ( # pylint: disable=no-name-in-module
jaeger,
)
@@ -186,3 +187,45 @@ def test_fields(self):
for call in mock_setter.mock_calls:
inject_fields.add(call[1][1])
self.assertEqual(FORMAT.fields, inject_fields)
+
+ def test_extract_no_trace_id_to_explicit_ctx(self):
+ carrier = {}
+ orig_ctx = Context({"k1": "v1"})
+
+ ctx = FORMAT.extract(carrier, orig_ctx)
+ self.assertDictEqual(orig_ctx, ctx)
+
+ def test_extract_no_trace_id_to_implicit_ctx(self):
+ carrier = {}
+
+ ctx = FORMAT.extract(carrier)
+ self.assertDictEqual(Context(), ctx)
+
+ def test_extract_invalid_uber_trace_id_header_to_explicit_ctx(self):
+ trace_id_headers = [
+ "000000000000000000000000deadbeef:00000000deadbef0:00",
+ "00000000000000000000000000000000:00000000deadbef0:00:00",
+ "000000000000000000000000deadbeef:0000000000000000:00:00",
+ "000000000000000000000000deadbeef:0000000000000000:00:xyz",
+ ]
+ for trace_id_header in trace_id_headers:
+ with self.subTest(trace_id_header=trace_id_header):
+ carrier = {"uber-trace-id": trace_id_header}
+ orig_ctx = Context({"k1": "v1"})
+
+ ctx = FORMAT.extract(carrier, orig_ctx)
+ self.assertDictEqual(orig_ctx, ctx)
+
+ def test_extract_invalid_uber_trace_id_header_to_implicit_ctx(self):
+ trace_id_headers = [
+ "000000000000000000000000deadbeef:00000000deadbef0:00",
+ "00000000000000000000000000000000:00000000deadbef0:00:00",
+ "000000000000000000000000deadbeef:0000000000000000:00:00",
+ "000000000000000000000000deadbeef:0000000000000000:00:xyz",
+ ]
+ for trace_id_header in trace_id_headers:
+ with self.subTest(trace_id_header=trace_id_header):
+ carrier = {"uber-trace-id": trace_id_header}
+
+ ctx = FORMAT.extract(carrier)
+ self.assertDictEqual(Context(), ctx)
diff --git a/scripts/eachdist.py b/scripts/eachdist.py
index 3a9b2b3b9e6..1cb8033d621 100755
--- a/scripts/eachdist.py
+++ b/scripts/eachdist.py
@@ -238,6 +238,17 @@ def setup_instparser(instparser):
"releaseargs", nargs=argparse.REMAINDER, help=extraargs_help("pytest")
)
+ fmtparser = subparsers.add_parser(
+ "format",
+ help="Formats all source code with black and isort.",
+ )
+ fmtparser.set_defaults(func=format_args)
+ fmtparser.add_argument(
+ "--path",
+ required=False,
+ help="Format only this path instead of entire repository",
+ )
+
return parser.parse_args(args)
@@ -649,6 +660,25 @@ def test_args(args):
)
+def format_args(args):
+ format_dir = str(find_projectroot())
+ if args.path:
+ format_dir = os.path.join(format_dir, args.path)
+
+ runsubprocess(
+ args.dry_run,
+ ("black", "."),
+ cwd=format_dir,
+ check=True,
+ )
+ runsubprocess(
+ args.dry_run,
+ ("isort", "--profile", "black", "."),
+ cwd=format_dir,
+ check=True,
+ )
+
+
def main():
args = parse_args()
args.func(args)
diff --git a/tests/util/src/opentelemetry/test/mock_textmap.py b/tests/util/src/opentelemetry/test/mock_textmap.py
index 4cdef447d6a..c3e901ee287 100644
--- a/tests/util/src/opentelemetry/test/mock_textmap.py
+++ b/tests/util/src/opentelemetry/test/mock_textmap.py
@@ -15,7 +15,7 @@
import typing
from opentelemetry import trace
-from opentelemetry.context import Context, get_current
+from opentelemetry.context import Context
from opentelemetry.propagators.textmap import (
CarrierT,
Getter,
@@ -39,7 +39,7 @@ def extract(
context: typing.Optional[Context] = None,
getter: Getter = default_getter,
) -> Context:
- return get_current()
+ return Context()
def inject(
self,
@@ -66,11 +66,13 @@ def extract(
context: typing.Optional[Context] = None,
getter: Getter = default_getter,
) -> Context:
+ if context is None:
+ context = Context()
trace_id_list = getter.get(carrier, self.TRACE_ID_KEY)
span_id_list = getter.get(carrier, self.SPAN_ID_KEY)
if not trace_id_list or not span_id_list:
- return trace.set_span_in_context(trace.INVALID_SPAN)
+ return context
return trace.set_span_in_context(
trace.NonRecordingSpan(
@@ -79,7 +81,8 @@ def extract(
span_id=int(span_id_list[0]),
is_remote=True,
)
- )
+ ),
+ context,
)
def inject(