From 0d200b354731a2e376e23fa491d45139f540a195 Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Mon, 19 May 2025 10:15:32 +0200 Subject: [PATCH 1/5] Patch TracerProvider --- sentry_sdk/opentelemetry/tracing.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/sentry_sdk/opentelemetry/tracing.py b/sentry_sdk/opentelemetry/tracing.py index 8392c1515a..df618b2781 100644 --- a/sentry_sdk/opentelemetry/tracing.py +++ b/sentry_sdk/opentelemetry/tracing.py @@ -1,12 +1,14 @@ from opentelemetry import trace from opentelemetry.propagate import set_global_textmap from opentelemetry.sdk.trace import TracerProvider, Span, ReadableSpan +from opentelemetry.trace import _TRACER_PROVIDER_SET_ONCE from sentry_sdk.opentelemetry import ( SentryPropagator, SentrySampler, SentrySpanProcessor, ) +from sentry_sdk.utils import logger def patch_readable_span(): @@ -28,8 +30,19 @@ def sentry_patched_readable_span(self): def setup_sentry_tracing(): # type: () -> None - provider = TracerProvider(sampler=SentrySampler()) - provider.add_span_processor(SentrySpanProcessor()) - trace.set_tracer_provider(provider) + + # TracerProvider can only be set once. If we're the first ones setting it, + # there's no issue. If it already exists, we need to patch it. + if _TRACER_PROVIDER_SET_ONCE._done: + logger.debug("[Tracing] Detected an existing TracerProvider, patching") + tracer_provider = trace.get_tracer_provider() + tracer_provider.add_span_processor(SentrySpanProcessor()) + tracer_provider.sampler = SentrySampler() + + else: + logger.debug("[Tracing] No TracerProvider set, creating a new one") + tracer_provider = TracerProvider(sampler=SentrySampler()) + tracer_provider.add_span_processor(SentrySpanProcessor()) + trace.set_tracer_provider(tracer_provider) set_global_textmap(SentryPropagator()) From 8fdcb7cbfb530f84ebc61abe51f6fddac2b3def9 Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Mon, 19 May 2025 14:42:10 +0200 Subject: [PATCH 2/5] Save OTel instrumentation_scope as origin --- sentry_sdk/opentelemetry/span_processor.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sentry_sdk/opentelemetry/span_processor.py b/sentry_sdk/opentelemetry/span_processor.py index b5279bccb0..45acfd0315 100644 --- a/sentry_sdk/opentelemetry/span_processor.py +++ b/sentry_sdk/opentelemetry/span_processor.py @@ -71,6 +71,7 @@ def on_start(self, span, parent_context=None): return self._add_root_span(span, get_current_span(parent_context)) + self._add_origin(span) self._start_profile(span) def on_end(self, span): @@ -97,6 +98,13 @@ def force_flush(self, timeout_millis=30000): # type: (int) -> bool return True + def _add_origin(self, span): + # type: (Span) -> None + if span.instrumentation_scope and span.instrumentation_scope.name: + span.set_attribute( + SentrySpanAttribute.ORIGIN, span.instrumentation_scope.name + ) + def _add_root_span(self, span, parent_span): # type: (Span, AbstractSpan) -> None """ From 8ccae1776c16eafbb9509a428429c59d47caaac3 Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Mon, 19 May 2025 15:37:07 +0200 Subject: [PATCH 3/5] reset tracer after each test --- tests/conftest.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/conftest.py b/tests/conftest.py index 5987265e32..2e1b234e69 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,6 +2,8 @@ import os import socket import warnings +from opentelemetry import trace as otel_trace +from opentelemetry.util._once import Once from threading import Thread from contextlib import contextmanager from http.server import BaseHTTPRequestHandler, HTTPServer @@ -81,6 +83,12 @@ def clean_scopes(): setup_initial_scopes() +def clean_tracer(autouse=True): + """Reset TracerProvider so that we can set it up from scratch.""" + otel_trace._TRACER_PROVIDER_SET_ONCE = Once() + otel_trace._TRACER_PROVIDER = None + + @pytest.fixture(autouse=True) def internal_exceptions(request): errors = [] From bfd9562ddf532f84ec41516784a791f257300f6d Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Mon, 19 May 2025 15:38:31 +0200 Subject: [PATCH 4/5] fix typo --- sentry_sdk/tracing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry_sdk/tracing.py b/sentry_sdk/tracing.py index 20c3f3ffac..b747dcc6f8 100644 --- a/sentry_sdk/tracing.py +++ b/sentry_sdk/tracing.py @@ -189,7 +189,7 @@ def __init__( If otel_span is passed explicitly, just acts as a proxy. If span is passed explicitly, use it. The only purpose of this param - if backwards compatibility with start_transaction(transaction=...). + is backwards compatibility with start_transaction(transaction=...). If only_if_parent is True, just return an INVALID_SPAN and avoid instrumentation if there's no active parent span. From b10834d14bf5aadade382f715aedff2f4741ad2b Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Mon, 19 May 2025 16:00:27 +0200 Subject: [PATCH 5/5] fix --- sentry_sdk/opentelemetry/tracing.py | 3 ++- tests/conftest.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/sentry_sdk/opentelemetry/tracing.py b/sentry_sdk/opentelemetry/tracing.py index df618b2781..3e3bbe556a 100644 --- a/sentry_sdk/opentelemetry/tracing.py +++ b/sentry_sdk/opentelemetry/tracing.py @@ -1,7 +1,6 @@ from opentelemetry import trace from opentelemetry.propagate import set_global_textmap from opentelemetry.sdk.trace import TracerProvider, Span, ReadableSpan -from opentelemetry.trace import _TRACER_PROVIDER_SET_ONCE from sentry_sdk.opentelemetry import ( SentryPropagator, @@ -33,6 +32,8 @@ def setup_sentry_tracing(): # TracerProvider can only be set once. If we're the first ones setting it, # there's no issue. If it already exists, we need to patch it. + from opentelemetry.trace import _TRACER_PROVIDER_SET_ONCE + if _TRACER_PROVIDER_SET_ONCE._done: logger.debug("[Tracing] Detected an existing TracerProvider, patching") tracer_provider = trace.get_tracer_provider() diff --git a/tests/conftest.py b/tests/conftest.py index 2e1b234e69..5e190b0cf4 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -83,7 +83,8 @@ def clean_scopes(): setup_initial_scopes() -def clean_tracer(autouse=True): +@pytest.fixture(autouse=True) +def clean_tracer(): """Reset TracerProvider so that we can set it up from scratch.""" otel_trace._TRACER_PROVIDER_SET_ONCE = Once() otel_trace._TRACER_PROVIDER = None