From 8a73f7770e2bb93751668ba01f0dca38fac03a53 Mon Sep 17 00:00:00 2001 From: Nathaniel Ruiz Nowell Date: Wed, 23 Sep 2020 15:26:17 -0700 Subject: [PATCH 1/2] Add interface to make trace & span id customizable --- docs/api/trace.ids_generator.rst | 7 +++ docs/api/trace.rst | 1 + opentelemetry-api/CHANGELOG.md | 2 + .../src/opentelemetry/trace/__init__.py | 3 ++ .../src/opentelemetry/trace/ids_generator.py | 52 +++++++++++++++++++ opentelemetry-sdk/CHANGELOG.md | 2 + .../src/opentelemetry/sdk/trace/__init__.py | 27 +++------- .../sdk/trace/propagation/b3_format.py | 6 +-- 8 files changed, 77 insertions(+), 23 deletions(-) create mode 100644 docs/api/trace.ids_generator.rst create mode 100644 opentelemetry-api/src/opentelemetry/trace/ids_generator.py diff --git a/docs/api/trace.ids_generator.rst b/docs/api/trace.ids_generator.rst new file mode 100644 index 00000000000..8f516bb3b1e --- /dev/null +++ b/docs/api/trace.ids_generator.rst @@ -0,0 +1,7 @@ +opentelemetry.trace.ids_generator +================================= + +.. automodule:: opentelemetry.trace.ids_generator + :members: + :undoc-members: + :show-inheritance: \ No newline at end of file diff --git a/docs/api/trace.rst b/docs/api/trace.rst index 65d9b4d8c88..81f11e3dd09 100644 --- a/docs/api/trace.rst +++ b/docs/api/trace.rst @@ -8,6 +8,7 @@ Submodules trace.status trace.span + trace.ids_generator Module contents --------------- diff --git a/opentelemetry-api/CHANGELOG.md b/opentelemetry-api/CHANGELOG.md index 3d0bcd80052..91a822890bd 100644 --- a/opentelemetry-api/CHANGELOG.md +++ b/opentelemetry-api/CHANGELOG.md @@ -6,6 +6,8 @@ ([#1123](https://github.com/open-telemetry/opentelemetry-python/pull/1123)) - Store `int`s as `int`s in the global Configuration object ([#1118](https://github.com/open-telemetry/opentelemetry-python/pull/1118)) +- Allow for Custom Trace and Span IDs Generation - `IdsGenerator` for TracerProvider + ([#1153](https://github.com/open-telemetry/opentelemetry-python/pull/1153)) ## Version 0.13b0 diff --git a/opentelemetry-api/src/opentelemetry/trace/__init__.py b/opentelemetry-api/src/opentelemetry/trace/__init__.py index c3c2098ff82..301314e28e1 100644 --- a/opentelemetry-api/src/opentelemetry/trace/__init__.py +++ b/opentelemetry-api/src/opentelemetry/trace/__init__.py @@ -77,6 +77,7 @@ from contextlib import contextmanager from logging import getLogger +from opentelemetry.trace.ids_generator import IdsGenerator, RandomIdsGenerator from opentelemetry.trace.propagation import ( get_current_span, set_span_in_context, @@ -436,6 +437,7 @@ def get_tracer_provider() -> TracerProvider: __all__ = [ "DEFAULT_TRACE_OPTIONS", "DEFAULT_TRACE_STATE", + "IdsGenerator", "INVALID_SPAN", "INVALID_SPAN_CONTEXT", "INVALID_SPAN_ID", @@ -446,6 +448,7 @@ def get_tracer_provider() -> TracerProvider: "Link", "LinkBase", "ParentSpan", + "RandomIdsGenerator", "Span", "SpanContext", "SpanKind", diff --git a/opentelemetry-api/src/opentelemetry/trace/ids_generator.py b/opentelemetry-api/src/opentelemetry/trace/ids_generator.py new file mode 100644 index 00000000000..31e1fee0788 --- /dev/null +++ b/opentelemetry-api/src/opentelemetry/trace/ids_generator.py @@ -0,0 +1,52 @@ +# 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 abc +import random + + +class IdsGenerator(abc.ABC): + @abc.abstractmethod + def generate_span_id(self) -> int: + """Get a new span ID. + + Returns: + A 64-bit int for use as a span ID + """ + + @abc.abstractmethod + def generate_trace_id(self) -> int: + """Get a new trace ID. + + Implementations should at least make the 64 least significant bits + uniformly random. Samplers like the `TraceIdRatioBased` sampler rely on + this randomness to make sampling decisions. + + See `the specification on TraceIdRatioBased `_. + + Returns: + A 128-bit int for use as a trace ID + """ + + +class RandomIdsGenerator(IdsGenerator): + """The default IDs generator for TracerProvider which randomly generates all + bits when generating IDs. + """ + + def generate_span_id(self) -> int: + return random.getrandbits(64) + + def generate_trace_id(self) -> int: + return random.getrandbits(128) diff --git a/opentelemetry-sdk/CHANGELOG.md b/opentelemetry-sdk/CHANGELOG.md index 1688de79b20..71864282885 100644 --- a/opentelemetry-sdk/CHANGELOG.md +++ b/opentelemetry-sdk/CHANGELOG.md @@ -8,6 +8,8 @@ ([#1128](https://github.com/open-telemetry/opentelemetry-python/pull/1128)) - Add support for `OTEL_BSP_MAX_QUEUE_SIZE`, `OTEL_BSP_SCHEDULE_DELAY_MILLIS`, `OTEL_BSP_MAX_EXPORT_BATCH_SIZE` and `OTEL_BSP_EXPORT_TIMEOUT_MILLIS` environment variables ([#1105](https://github.com/open-telemetry/opentelemetry-python/pull/1120)) +- Allow for Custom Trace and Span IDs Generation - `IdsGenerator` for TracerProvider + ([#1153](https://github.com/open-telemetry/opentelemetry-python/pull/1153)) ## Version 0.13b0 diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py index 13819ed35b0..0134ec7e775 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py @@ -663,24 +663,6 @@ def record_exception(self, exception: Exception) -> None: ) -def generate_span_id() -> int: - """Get a new random span ID. - - Returns: - A random 64-bit int for use as a span ID - """ - return random.getrandbits(64) - - -def generate_trace_id() -> int: - """Get a new random trace ID. - - Returns: - A random 128-bit int for use as a trace ID - """ - return random.getrandbits(128) - - class Tracer(trace_api.Tracer): """See `opentelemetry.trace.Tracer`. @@ -733,7 +715,7 @@ def start_span( # pylint: disable=too-many-locals if parent_context is None or not parent_context.is_valid: parent = parent_context = None - trace_id = generate_trace_id() + trace_id = self.source.ids_generator.generate_trace_id() trace_flags = None trace_state = None else: @@ -757,7 +739,7 @@ def start_span( # pylint: disable=too-many-locals ) context = trace_api.SpanContext( trace_id, - generate_span_id(), + self.source.ids_generator.generate_span_id(), is_remote=False, trace_flags=trace_flags, trace_state=trace_state, @@ -826,10 +808,15 @@ def __init__( active_span_processor: Union[ SynchronousMultiSpanProcessor, ConcurrentMultiSpanProcessor ] = None, + ids_generator: trace_api.IdsGenerator = None, ): self._active_span_processor = ( active_span_processor or SynchronousMultiSpanProcessor() ) + if ids_generator is None: + self.ids_generator = trace_api.RandomIdsGenerator() + else: + self.ids_generator = ids_generator self.resource = resource self.sampler = sampler self._atexit_handler = None diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/propagation/b3_format.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/propagation/b3_format.py index c2b12f33f5a..813b6e85600 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/propagation/b3_format.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/propagation/b3_format.py @@ -17,7 +17,6 @@ import opentelemetry.trace as trace from opentelemetry.context import Context -from opentelemetry.sdk.trace import generate_span_id, generate_trace_id from opentelemetry.trace.propagation.textmap import ( Getter, Setter, @@ -103,8 +102,9 @@ def extract( self._trace_id_regex.fullmatch(trace_id) is None or self._span_id_regex.fullmatch(span_id) is None ): - trace_id = generate_trace_id() - span_id = generate_span_id() + ids_generator = trace.get_tracer_provider().ids_generator + trace_id = ids_generator.generate_trace_id() + span_id = ids_generator.generate_span_id() sampled = "0" else: From 56e9ac5651a3b22d1ab68aac0744492322d34111 Mon Sep 17 00:00:00 2001 From: Nathaniel Ruiz Nowell Date: Wed, 23 Sep 2020 20:22:00 -0700 Subject: [PATCH 2/2] Update tests to use new ids generator --- .../tests/test_flask_example.py | 6 ++--- .../tests/test_datadog_format.py | 9 ++++--- .../tests/trace/propagation/test_b3_format.py | 25 +++++++++++++------ opentelemetry-sdk/tests/trace/test_trace.py | 9 ++++--- 4 files changed, 30 insertions(+), 19 deletions(-) diff --git a/docs/examples/opentelemetry-example-app/tests/test_flask_example.py b/docs/examples/opentelemetry-example-app/tests/test_flask_example.py index bc903a9c60c..124a79c0eff 100644 --- a/docs/examples/opentelemetry-example-app/tests/test_flask_example.py +++ b/docs/examples/opentelemetry-example-app/tests/test_flask_example.py @@ -21,7 +21,6 @@ import opentelemetry_example_app.flask_example as flask_example from opentelemetry import trace -from opentelemetry.sdk import trace as trace_sdk class TestFlaskExample(unittest.TestCase): @@ -46,7 +45,8 @@ def tearDown(self): self.send_patcher.stop() def test_full_path(self): - trace_id = trace_sdk.generate_trace_id() + ids_generator = trace.RandomIdsGenerator() + trace_id = ids_generator.generate_trace_id() # We need to use the Werkzeug test app because # The headers are injected at the wsgi layer. # The flask test app will not include these, and @@ -58,7 +58,7 @@ def test_full_path(self): headers={ "traceparent": "00-{:032x}-{:016x}-{:02x}".format( trace_id, - trace_sdk.generate_span_id(), + ids_generator.generate_span_id(), trace.TraceFlags.SAMPLED, ) }, diff --git a/exporter/opentelemetry-exporter-datadog/tests/test_datadog_format.py b/exporter/opentelemetry-exporter-datadog/tests/test_datadog_format.py index 1a398745b8d..994cac2d602 100644 --- a/exporter/opentelemetry-exporter-datadog/tests/test_datadog_format.py +++ b/exporter/opentelemetry-exporter-datadog/tests/test_datadog_format.py @@ -30,11 +30,12 @@ def get_as_list(dict_object, key): class TestDatadogFormat(unittest.TestCase): @classmethod def setUpClass(cls): + ids_generator = trace_api.RandomIdsGenerator() cls.serialized_trace_id = propagator.format_trace_id( - trace.generate_trace_id() + ids_generator.generate_trace_id() ) cls.serialized_parent_id = propagator.format_span_id( - trace.generate_span_id() + ids_generator.generate_span_id() ) cls.serialized_origin = "origin-service" @@ -107,7 +108,7 @@ def test_context_propagation(self): "child", trace_api.SpanContext( parent_context.trace_id, - trace.generate_span_id(), + trace_api.RandomIdsGenerator().generate_span_id(), is_remote=False, trace_flags=parent_context.trace_flags, trace_state=parent_context.trace_state, @@ -152,7 +153,7 @@ def test_sampling_priority_auto_reject(self): "child", trace_api.SpanContext( parent_context.trace_id, - trace.generate_span_id(), + trace_api.RandomIdsGenerator().generate_span_id(), is_remote=False, trace_flags=parent_context.trace_flags, trace_state=parent_context.trace_state, diff --git a/opentelemetry-sdk/tests/trace/propagation/test_b3_format.py b/opentelemetry-sdk/tests/trace/propagation/test_b3_format.py index 77834adec97..07b3010087a 100644 --- a/opentelemetry-sdk/tests/trace/propagation/test_b3_format.py +++ b/opentelemetry-sdk/tests/trace/propagation/test_b3_format.py @@ -38,7 +38,7 @@ def get_child_parent_new_carrier(old_carrier): "child", trace_api.SpanContext( parent_context.trace_id, - trace.generate_span_id(), + trace_api.RandomIdsGenerator().generate_span_id(), is_remote=False, trace_flags=parent_context.trace_flags, trace_state=parent_context.trace_state, @@ -56,14 +56,15 @@ def get_child_parent_new_carrier(old_carrier): class TestB3Format(unittest.TestCase): @classmethod def setUpClass(cls): + ids_generator = trace_api.RandomIdsGenerator() cls.serialized_trace_id = b3_format.format_trace_id( - trace.generate_trace_id() + ids_generator.generate_trace_id() ) cls.serialized_span_id = b3_format.format_span_id( - trace.generate_span_id() + ids_generator.generate_span_id() ) cls.serialized_parent_id = b3_format.format_span_id( - trace.generate_span_id() + ids_generator.generate_span_id() ) def test_extract_multi_header(self): @@ -246,8 +247,12 @@ def test_missing_trace_id(self): span_context = trace_api.get_current_span(ctx).get_context() self.assertEqual(span_context.trace_id, trace_api.INVALID_TRACE_ID) - @patch("opentelemetry.sdk.trace.propagation.b3_format.generate_trace_id") - @patch("opentelemetry.sdk.trace.propagation.b3_format.generate_span_id") + @patch( + "opentelemetry.sdk.trace.propagation.b3_format.trace.RandomIdsGenerator.generate_trace_id" + ) + @patch( + "opentelemetry.sdk.trace.propagation.b3_format.trace.RandomIdsGenerator.generate_span_id" + ) def test_invalid_trace_id( self, mock_generate_span_id, mock_generate_trace_id ): @@ -268,8 +273,12 @@ def test_invalid_trace_id( self.assertEqual(span_context.trace_id, 1) self.assertEqual(span_context.span_id, 2) - @patch("opentelemetry.sdk.trace.propagation.b3_format.generate_trace_id") - @patch("opentelemetry.sdk.trace.propagation.b3_format.generate_span_id") + @patch( + "opentelemetry.sdk.trace.propagation.b3_format.trace.RandomIdsGenerator.generate_trace_id" + ) + @patch( + "opentelemetry.sdk.trace.propagation.b3_format.trace.RandomIdsGenerator.generate_span_id" + ) def test_invalid_span_id( self, mock_generate_span_id, mock_generate_trace_id ): diff --git a/opentelemetry-sdk/tests/trace/test_trace.py b/opentelemetry-sdk/tests/trace/test_trace.py index fdf85ef19b5..8c5544578d4 100644 --- a/opentelemetry-sdk/tests/trace/test_trace.py +++ b/opentelemetry-sdk/tests/trace/test_trace.py @@ -615,14 +615,15 @@ def test_invalid_event_attributes(self): self.assertEqual(root.events[3].attributes, {"attr2": (1, 2)}) def test_links(self): + ids_generator = trace_api.RandomIdsGenerator() other_context1 = trace_api.SpanContext( - trace_id=trace.generate_trace_id(), - span_id=trace.generate_span_id(), + trace_id=ids_generator.generate_trace_id(), + span_id=ids_generator.generate_span_id(), is_remote=False, ) other_context2 = trace_api.SpanContext( - trace_id=trace.generate_trace_id(), - span_id=trace.generate_span_id(), + trace_id=ids_generator.generate_trace_id(), + span_id=ids_generator.generate_span_id(), is_remote=False, )