diff --git a/sentry_sdk/opentelemetry/span_processor.py b/sentry_sdk/opentelemetry/span_processor.py index a148fb0f62..d70ae3dbe5 100644 --- a/sentry_sdk/opentelemetry/span_processor.py +++ b/sentry_sdk/opentelemetry/span_processor.py @@ -72,6 +72,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): @@ -98,6 +99,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 """ diff --git a/sentry_sdk/opentelemetry/tracing.py b/sentry_sdk/opentelemetry/tracing.py index 8392c1515a..3e3bbe556a 100644 --- a/sentry_sdk/opentelemetry/tracing.py +++ b/sentry_sdk/opentelemetry/tracing.py @@ -7,6 +7,7 @@ SentrySampler, SentrySpanProcessor, ) +from sentry_sdk.utils import logger def patch_readable_span(): @@ -28,8 +29,21 @@ 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. + 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() + 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()) diff --git a/sentry_sdk/tracing.py b/sentry_sdk/tracing.py index 7a5f12fe91..f15f07065a 100644 --- a/sentry_sdk/tracing.py +++ b/sentry_sdk/tracing.py @@ -188,7 +188,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. diff --git a/tests/conftest.py b/tests/conftest.py index d64652c4e9..04edaf4907 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 http.server import BaseHTTPRequestHandler, HTTPServer @@ -73,6 +75,13 @@ def clean_scopes(): setup_initial_scopes() +@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 + + @pytest.fixture(autouse=True) def internal_exceptions(request): errors = []