From 24ea60f9c13550cb8dae4c902e5326ef1dd88131 Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Fri, 30 Aug 2024 14:15:59 +0200 Subject: [PATCH 1/3] wip --- .../integrations/opentelemetry/scope.py | 36 ++++++++- sentry_sdk/scope.py | 80 +++++++++---------- 2 files changed, 69 insertions(+), 47 deletions(-) diff --git a/sentry_sdk/integrations/opentelemetry/scope.py b/sentry_sdk/integrations/opentelemetry/scope.py index 01bf693611..5ab5c70b5a 100644 --- a/sentry_sdk/integrations/opentelemetry/scope.py +++ b/sentry_sdk/integrations/opentelemetry/scope.py @@ -4,19 +4,20 @@ from opentelemetry.context import get_value, set_value, attach, detach, get_current from opentelemetry.trace import SpanContext, NonRecordingSpan, TraceFlags, use_span -from sentry_sdk.scope import Scope, ScopeType -from sentry_sdk.tracing import POTelSpan from sentry_sdk.integrations.opentelemetry.consts import ( SENTRY_SCOPES_KEY, SENTRY_FORK_ISOLATION_SCOPE_KEY, ) - +from sentry_sdk.scope import Scope, ScopeType +from sentry_sdk.tracing import POTelSpan from sentry_sdk._types import TYPE_CHECKING if TYPE_CHECKING: from typing import Tuple, Optional, Generator, Dict, Any + from typing_extensions import Unpack from sentry_sdk._types import SamplingContext + from sentry_sdk.tracing import TransactionKwargs class PotelScope(Scope): @@ -62,6 +63,19 @@ def _get_isolation_scope(cls): scopes = cls._get_scopes() return scopes[1] if scopes else None + def start_transaction(self, custom_sampling_context=None, **kwargs): + # type: (Optional[SamplingContext], Unpack[TransactionKwargs]) -> POTelSpan + """ + .. deprecated:: 3.0.0 + This function is deprecated and will be removed in a future release. + Use :py:meth:`sentry_sdk.start_span` instead. + """ + return self.start_span(custom_sampling_context) + + def start_span(self, custom_sampling_context=None, **kwargs): + # type: (Optional[SamplingContext], Any) -> POTelSpan + return POTelSpan(**kwargs, scope=self) + @contextmanager def continue_trace(self, environ_or_headers): # type: (Dict[str, Any]) -> Generator[None, None, None] @@ -103,6 +117,22 @@ def start_span(self, custom_sampling_context=None, **kwargs): # TODO-neel-potel ideally want to remove the span argument, discuss with ivana return POTelSpan(**kwargs, scope=self) + @property + def root_span(self): + # type: () -> POTelSpan + """Return the root span in the scope, if any.""" + + # there is no span on the scope + if self._span is None: + return None + + # this is a root span + if self._span.root_span is None: + return self._span + + # get the topmost parent + return self._span.root_span + _INITIAL_CURRENT_SCOPE = PotelScope(ty=ScopeType.CURRENT) _INITIAL_ISOLATION_SCOPE = PotelScope(ty=ScopeType.ISOLATION) diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 783a099b98..688aff9d52 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -25,7 +25,6 @@ NoOpSpan, Span, Transaction, - POTelSpan, ) from sentry_sdk.utils import ( capture_internal_exception, @@ -696,13 +695,18 @@ def fingerprint(self, value): def transaction(self): # type: () -> Any # would be type: () -> Optional[Transaction], see https://github.com/python/mypy/issues/3004 - """ - Return the transaction (root span) in the scope, if any. + """Return the transaction (root span) in the scope, if any.""" - .. deprecated:: 3.0.0 - This property is deprecated. Use root_span instead. - """ - return self.root_span + # there is no span/transaction on the scope + if self._span is None: + return None + + # there is an orphan span on the scope + if self._span.containing_transaction is None: + return None + # there is either a transaction (which is its own containing + # transaction) or a non-orphan span on the scope + return self._span.containing_transaction @transaction.setter def transaction(self, value): @@ -729,22 +733,6 @@ def transaction(self, value): if self._span and self._span.containing_transaction: self._span.containing_transaction.name = value - @property - def root_span(self): - # type: () -> POTelSpan - """Return the root span in the scope, if any.""" - - # there is no span on the scope - if self._span is None: - return None - - # this is a root span - if self._span.root_span is None: - return self._span - - # get the topmost parent - return self._span.root_span - def set_transaction_name(self, name, source=None): # type: (str, Optional[str]) -> None """Set the transaction name and optionally the transaction source.""" @@ -953,10 +941,6 @@ def start_transaction( ): # type: (Optional[Transaction], Optional[SamplingContext], Unpack[TransactionKwargs]) -> Union[Transaction, NoOpSpan] """ - .. deprecated:: 3.0.0 - This function is deprecated and will be removed in a future release. - Use :py:meth:`sentry_sdk.start_span` instead. - Start and return a transaction. Start an existing transaction if given, otherwise create and start a new @@ -987,12 +971,14 @@ def start_transaction( """ kwargs.setdefault("scope", self) + client = self.get_client() + try_autostart_continuous_profiler() custom_sampling_context = custom_sampling_context or {} # if we haven't been given a transaction, make one - transaction = transaction or POTelSpan(**kwargs) + transaction = Transaction(**kwargs) # use traces_sample_rate, traces_sampler, and/or inheritance to make a # sampling decision @@ -1011,10 +997,15 @@ def start_transaction( transaction._profile = profile + # we don't bother to keep spans if we already know we're not going to + # send the transaction + max_spans = (client.options["_experiments"].get("max_spans")) or 1000 + transaction.init_span_recorder(maxlen=max_spans) + return transaction - def start_span(self, span=None, custom_sampling_context=None, **kwargs): - # type: (Optional[Span], Optional[SamplingContext], Any) -> Span + def start_span(self, **kwargs): + # type: (Optional[Span], Any) -> Span """ Start a span whose parent is the currently active span, if any. @@ -1024,24 +1015,25 @@ def start_span(self, span=None, custom_sampling_context=None, **kwargs): For supported `**kwargs` see :py:class:`sentry_sdk.tracing.Span`. """ - kwargs.setdefault("scope", self) + with new_scope(): + kwargs.setdefault("scope", self) - # get current span or transaction - span = span or self.span or self.get_isolation_scope().span + # get current span or transaction + span = self.span or self.get_isolation_scope().span - if span is None: - # New spans get the `trace_id` from the scope - if "trace_id" not in kwargs: - propagation_context = self.get_active_propagation_context() - if propagation_context is not None: - kwargs["trace_id"] = propagation_context.trace_id + if span is None: + # New spans get the `trace_id` from the scope + if "trace_id" not in kwargs: + propagation_context = self.get_active_propagation_context() + if propagation_context is not None: + kwargs["trace_id"] = propagation_context.trace_id - span = POTelSpan(**kwargs) - else: - # Children take `trace_id`` from the parent span. - span = span.start_child(**kwargs) + span = Span(**kwargs) + else: + # Children take `trace_id`` from the parent span. + span = span.start_child(**kwargs) - return span + return span def continue_trace( self, environ_or_headers, op=None, name=None, source=None, origin=None From 6b91cab3cac1f737f471985508384ac1733063b2 Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Fri, 30 Aug 2024 14:19:10 +0200 Subject: [PATCH 2/3] cleanup --- .../integrations/opentelemetry/scope.py | 39 +++++-------------- 1 file changed, 9 insertions(+), 30 deletions(-) diff --git a/sentry_sdk/integrations/opentelemetry/scope.py b/sentry_sdk/integrations/opentelemetry/scope.py index 5ab5c70b5a..32eca16969 100644 --- a/sentry_sdk/integrations/opentelemetry/scope.py +++ b/sentry_sdk/integrations/opentelemetry/scope.py @@ -63,19 +63,6 @@ def _get_isolation_scope(cls): scopes = cls._get_scopes() return scopes[1] if scopes else None - def start_transaction(self, custom_sampling_context=None, **kwargs): - # type: (Optional[SamplingContext], Unpack[TransactionKwargs]) -> POTelSpan - """ - .. deprecated:: 3.0.0 - This function is deprecated and will be removed in a future release. - Use :py:meth:`sentry_sdk.start_span` instead. - """ - return self.start_span(custom_sampling_context) - - def start_span(self, custom_sampling_context=None, **kwargs): - # type: (Optional[SamplingContext], Any) -> POTelSpan - return POTelSpan(**kwargs, scope=self) - @contextmanager def continue_trace(self, environ_or_headers): # type: (Dict[str, Any]) -> Generator[None, None, None] @@ -112,27 +99,19 @@ def _incoming_otel_span_context(self): return span_context + def start_transaction(self, custom_sampling_context=None, **kwargs): + # type: (Optional[SamplingContext], Unpack[TransactionKwargs]) -> POTelSpan + """ + .. deprecated:: 3.0.0 + This function is deprecated and will be removed in a future release. + Use :py:meth:`sentry_sdk.start_span` instead. + """ + return self.start_span(custom_sampling_context) + def start_span(self, custom_sampling_context=None, **kwargs): # type: (Optional[SamplingContext], Any) -> POTelSpan - # TODO-neel-potel ideally want to remove the span argument, discuss with ivana return POTelSpan(**kwargs, scope=self) - @property - def root_span(self): - # type: () -> POTelSpan - """Return the root span in the scope, if any.""" - - # there is no span on the scope - if self._span is None: - return None - - # this is a root span - if self._span.root_span is None: - return self._span - - # get the topmost parent - return self._span.root_span - _INITIAL_CURRENT_SCOPE = PotelScope(ty=ScopeType.CURRENT) _INITIAL_ISOLATION_SCOPE = PotelScope(ty=ScopeType.ISOLATION) From 3461028da38d5f53205fadf94066db408888cd8e Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Fri, 30 Aug 2024 14:33:35 +0200 Subject: [PATCH 3/3] . --- sentry_sdk/integrations/opentelemetry/scope.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry_sdk/integrations/opentelemetry/scope.py b/sentry_sdk/integrations/opentelemetry/scope.py index 32eca16969..1d03d67c35 100644 --- a/sentry_sdk/integrations/opentelemetry/scope.py +++ b/sentry_sdk/integrations/opentelemetry/scope.py @@ -106,7 +106,7 @@ def start_transaction(self, custom_sampling_context=None, **kwargs): This function is deprecated and will be removed in a future release. Use :py:meth:`sentry_sdk.start_span` instead. """ - return self.start_span(custom_sampling_context) + return self.start_span(custom_sampling_context=custom_sampling_context) def start_span(self, custom_sampling_context=None, **kwargs): # type: (Optional[SamplingContext], Any) -> POTelSpan