diff --git a/sentry_sdk/api.py b/sentry_sdk/api.py index 0b88ea3274..36cefb9a57 100644 --- a/sentry_sdk/api.py +++ b/sentry_sdk/api.py @@ -1,4 +1,5 @@ import inspect +from contextlib import contextmanager from sentry_sdk import tracing_utils, Client from sentry_sdk._init_implementation import init @@ -23,6 +24,7 @@ from typing import Callable from typing import TypeVar from typing import Union + from typing import Generator from typing_extensions import Unpack @@ -336,11 +338,11 @@ def get_baggage(): return None -def continue_trace(environ_or_headers, op=None, name=None, source=None, origin=None): - # type: (Dict[str, Any], Optional[str], Optional[str], Optional[str], Optional[str]) -> Transaction +@contextmanager +def continue_trace(environ_or_headers): + # type: (Dict[str, Any]) -> Generator[None, None, None] """ - Sets the propagation context from environment or headers and returns a transaction. + Sets the propagation context from environment or headers to continue an incoming trace. """ - return get_isolation_scope().continue_trace( - environ_or_headers, op, name, source, origin - ) + with get_isolation_scope().continue_trace(environ_or_headers): + yield diff --git a/sentry_sdk/integrations/opentelemetry/scope.py b/sentry_sdk/integrations/opentelemetry/scope.py index 6d6f8f6acf..c1eacc3852 100644 --- a/sentry_sdk/integrations/opentelemetry/scope.py +++ b/sentry_sdk/integrations/opentelemetry/scope.py @@ -12,7 +12,7 @@ from sentry_sdk._types import TYPE_CHECKING if TYPE_CHECKING: - from typing import Tuple, Optional, Generator + from typing import Tuple, Optional, Generator, Dict, Any class PotelScope(Scope): @@ -58,6 +58,14 @@ def _get_isolation_scope(cls): scopes = cls._get_scopes() return scopes[1] if scopes else None + @contextmanager + def continue_trace(self, environ_or_headers): + # type: (Dict[str, Any]) -> Generator[None, None, None] + with new_scope() as scope: + scope.generate_propagation_context(environ_or_headers) + # TODO-neel-potel add remote span on context + yield + _INITIAL_CURRENT_SCOPE = PotelScope(ty=ScopeType.CURRENT) _INITIAL_ISOLATION_SCOPE = PotelScope(ty=ScopeType.ISOLATION) diff --git a/sentry_sdk/integrations/wsgi.py b/sentry_sdk/integrations/wsgi.py index 9ea83a629c..cd3b53e805 100644 --- a/sentry_sdk/integrations/wsgi.py +++ b/sentry_sdk/integrations/wsgi.py @@ -91,26 +91,25 @@ def __call__(self, environ, start_response): ) ) - transaction = continue_trace( - environ, - op=OP.HTTP_SERVER, - name="generic WSGI request", - source=TRANSACTION_SOURCE_ROUTE, - origin=self.span_origin, - ) - - with sentry_sdk.start_transaction( - transaction, custom_sampling_context={"wsgi_environ": environ} - ): - try: - response = self.app( - environ, - partial( - _sentry_start_response, start_response, transaction - ), - ) - except BaseException: - reraise(*_capture_exception()) + with continue_trace(environ): + with sentry_sdk.start_transaction( + op=OP.HTTP_SERVER, + name="generic WSGI request", + source=TRANSACTION_SOURCE_ROUTE, + origin=self.span_origin, + custom_sampling_context={"wsgi_environ": environ}, + ) as transaction: + try: + response = self.app( + environ, + partial( + _sentry_start_response, + start_response, + transaction, + ), + ) + except BaseException: + reraise(*_capture_exception()) finally: _wsgi_middleware_applied.set(False) diff --git a/sentry_sdk/tracing.py b/sentry_sdk/tracing.py index c5812c9864..c04b51a344 100644 --- a/sentry_sdk/tracing.py +++ b/sentry_sdk/tracing.py @@ -89,7 +89,7 @@ class SpanKwargs(TypedDict, total=False): scope: "sentry_sdk.Scope" """The scope to use for this span. If not provided, we use the current scope.""" - origin: str + origin: Optional[str] """ The origin of the span. See https://develop.sentry.dev/sdk/performance/trace-origin/ @@ -1401,6 +1401,32 @@ def source(self, value): # type: (str) -> None pass + @property + def start_timestamp(self): + # type: () -> Optional[datetime] + start_time = self._otel_span.start_time + if start_time is None: + return None + + from sentry_sdk.integrations.opentelemetry.utils import ( + convert_from_otel_timestamp, + ) + + return convert_from_otel_timestamp(start_time) + + @property + def timestamp(self): + # type: () -> Optional[datetime] + end_time = self._otel_span.end_time + if end_time is None: + return None + + from sentry_sdk.integrations.opentelemetry.utils import ( + convert_from_otel_timestamp, + ) + + return convert_from_otel_timestamp(end_time) + def start_child(self, **kwargs): # type: (str, **Any) -> POTelSpan kwargs.setdefault("sampled", self.sampled) @@ -1485,7 +1511,7 @@ def set_status(self, status): otel_description = None else: otel_status = StatusCode.ERROR - otel_description = status.value + otel_description = status self._otel_span.set_status(otel_status, otel_description)