diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index 027600a765..87a81313eb 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -162,7 +162,7 @@ Looking to upgrade from Sentry SDK 2.x to 3.x? Here's a comprehensive list of wh - `same_process_as_parent` - `span_id` - `parent_span_id`: you can supply a `parent_span` instead -- Setting `Scope.transaction` directly is no longer supported. Use `Scope.set_transaction_name()` instead. +- The `Scope.transaction` property has been removed. To obtain the root span (previously transaction), use `Scope.root_span`. To set the root span's (transaction's) name, use `Scope.set_transaction_name()`. - Passing a list or `None` for `failed_request_status_codes` in the Starlette integration is no longer supported. Pass a set of integers instead. - The `span` argument of `Scope.trace_propagation_meta` is no longer supported. - Setting `Scope.user` directly is no longer supported. Use `Scope.set_user()` instead. diff --git a/sentry_sdk/api.py b/sentry_sdk/api.py index ca4ad3846a..475bbf5ef5 100644 --- a/sentry_sdk/api.py +++ b/sentry_sdk/api.py @@ -299,7 +299,7 @@ def start_transaction( def set_measurement(name, value, unit=""): # type: (str, float, MeasurementUnit) -> None - transaction = get_current_scope().transaction + transaction = get_current_scope().root_span if transaction is not None: transaction.set_measurement(name, value, unit) diff --git a/sentry_sdk/client.py b/sentry_sdk/client.py index f2acb8d489..c4599cd840 100644 --- a/sentry_sdk/client.py +++ b/sentry_sdk/client.py @@ -858,7 +858,7 @@ def _capture_experimental_log(self, current_scope, log): log["attributes"]["sentry.trace.parent_span_id"] = span.span_id if log.get("trace_id") is None: - transaction = current_scope.transaction + transaction = current_scope.root_span propagation_context = isolation_scope.get_active_propagation_context() if transaction is not None: log["trace_id"] = transaction.trace_id diff --git a/sentry_sdk/integrations/arq.py b/sentry_sdk/integrations/arq.py index ee80d211f5..e9dd9d92ac 100644 --- a/sentry_sdk/integrations/arq.py +++ b/sentry_sdk/integrations/arq.py @@ -127,12 +127,12 @@ def _capture_exception(exc_info): # type: (ExcInfo) -> None scope = sentry_sdk.get_current_scope() - if scope.transaction is not None: + if scope.root_span is not None: if exc_info[0] in ARQ_CONTROL_FLOW_EXCEPTIONS: - scope.transaction.set_status(SPANSTATUS.ABORTED) + scope.root_span.set_status(SPANSTATUS.ABORTED) return - scope.transaction.set_status(SPANSTATUS.INTERNAL_ERROR) + scope.root_span.set_status(SPANSTATUS.INTERNAL_ERROR) event, hint = event_from_exception( exc_info, @@ -149,8 +149,8 @@ def event_processor(event, hint): with capture_internal_exceptions(): scope = sentry_sdk.get_current_scope() - if scope.transaction is not None: - scope.transaction.name = ctx["job_name"] + if scope.root_span is not None: + scope.root_span.name = ctx["job_name"] event["transaction"] = ctx["job_name"] tags = event.setdefault("tags", {}) diff --git a/sentry_sdk/integrations/django/asgi.py b/sentry_sdk/integrations/django/asgi.py index 0ca1c080fd..511de34855 100644 --- a/sentry_sdk/integrations/django/asgi.py +++ b/sentry_sdk/integrations/django/asgi.py @@ -176,8 +176,8 @@ def wrap_async_view(callback): async def sentry_wrapped_callback(request, *args, **kwargs): # type: (Any, *Any, **Any) -> Any current_scope = sentry_sdk.get_current_scope() - if current_scope.transaction is not None: - current_scope.transaction.update_active_thread() + if current_scope.root_span is not None: + current_scope.root_span.update_active_thread() sentry_scope = sentry_sdk.get_isolation_scope() if sentry_scope.profile is not None: diff --git a/sentry_sdk/integrations/django/views.py b/sentry_sdk/integrations/django/views.py index aa2140764c..6240ac6bbb 100644 --- a/sentry_sdk/integrations/django/views.py +++ b/sentry_sdk/integrations/django/views.py @@ -79,8 +79,8 @@ def _wrap_sync_view(callback): def sentry_wrapped_callback(request, *args, **kwargs): # type: (Any, *Any, **Any) -> Any current_scope = sentry_sdk.get_current_scope() - if current_scope.transaction is not None: - current_scope.transaction.update_active_thread() + if current_scope.root_span is not None: + current_scope.root_span.update_active_thread() sentry_scope = sentry_sdk.get_isolation_scope() # set the active thread id to the handler thread for sync views diff --git a/sentry_sdk/integrations/fastapi.py b/sentry_sdk/integrations/fastapi.py index 76c6adee0f..4a0080c6fd 100644 --- a/sentry_sdk/integrations/fastapi.py +++ b/sentry_sdk/integrations/fastapi.py @@ -89,8 +89,8 @@ def _sentry_get_request_handler(*args, **kwargs): def _sentry_call(*args, **kwargs): # type: (*Any, **Any) -> Any current_scope = sentry_sdk.get_current_scope() - if current_scope.transaction is not None: - current_scope.transaction.update_active_thread() + if current_scope.root_span is not None: + current_scope.root_span.update_active_thread() sentry_scope = sentry_sdk.get_isolation_scope() if sentry_scope.profile is not None: diff --git a/sentry_sdk/integrations/huey.py b/sentry_sdk/integrations/huey.py index 8e9d45a526..fcdbd4f9f9 100644 --- a/sentry_sdk/integrations/huey.py +++ b/sentry_sdk/integrations/huey.py @@ -111,10 +111,10 @@ def _capture_exception(exc_info): scope = sentry_sdk.get_current_scope() if exc_info[0] in HUEY_CONTROL_FLOW_EXCEPTIONS: - scope.transaction.set_status(SPANSTATUS.ABORTED) + scope.root_span.set_status(SPANSTATUS.ABORTED) return - scope.transaction.set_status(SPANSTATUS.INTERNAL_ERROR) + scope.root_span.set_status(SPANSTATUS.INTERNAL_ERROR) event, hint = event_from_exception( exc_info, client_options=sentry_sdk.get_client().options, @@ -136,7 +136,7 @@ def _sentry_execute(*args, **kwargs): _capture_exception(exc_info) reraise(*exc_info) else: - sentry_sdk.get_current_scope().transaction.set_status(SPANSTATUS.OK) + sentry_sdk.get_current_scope().root_span.set_status(SPANSTATUS.OK) return result diff --git a/sentry_sdk/integrations/quart.py b/sentry_sdk/integrations/quart.py index 51306bb4cd..f7f8ce7902 100644 --- a/sentry_sdk/integrations/quart.py +++ b/sentry_sdk/integrations/quart.py @@ -122,8 +122,8 @@ def decorator(old_func): def _sentry_func(*args, **kwargs): # type: (*Any, **Any) -> Any current_scope = sentry_sdk.get_current_scope() - if current_scope.transaction is not None: - current_scope.transaction.update_active_thread() + if current_scope.root_span is not None: + current_scope.root_span.update_active_thread() sentry_scope = sentry_sdk.get_isolation_scope() if sentry_scope.profile is not None: diff --git a/sentry_sdk/integrations/starlette.py b/sentry_sdk/integrations/starlette.py index e75889ea38..bdb842f596 100644 --- a/sentry_sdk/integrations/starlette.py +++ b/sentry_sdk/integrations/starlette.py @@ -477,8 +477,8 @@ def _sentry_sync_func(*args, **kwargs): return old_func(*args, **kwargs) current_scope = sentry_sdk.get_current_scope() - if current_scope.transaction is not None: - current_scope.transaction.update_active_thread() + if current_scope.root_span is not None: + current_scope.root_span.update_active_thread() sentry_scope = sentry_sdk.get_isolation_scope() if sentry_scope.profile is not None: diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 4d69b2ff68..487b45b583 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -688,10 +688,10 @@ def fingerprint(self, value): self._fingerprint = value @property - def transaction(self): + def root_span(self): # type: () -> Any # would be type: () -> Optional[Span], see https://github.com/python/mypy/issues/3004 - """Return the transaction (root span) in the scope, if any.""" + """Return the root span in the scope, if any.""" # there is no span/transaction on the scope if self._span is None: diff --git a/tests/test_scope.py b/tests/test_scope.py index 98b9320944..c5a2d9380e 100644 --- a/tests/test_scope.py +++ b/tests/test_scope.py @@ -915,3 +915,18 @@ def test_last_event_id_cleared(sentry_init): Scope.get_isolation_scope().clear() assert Scope.last_event_id() is None, "last_event_id should be cleared" + + +def test_root_span(sentry_init): + sentry_init(traces_sample_rate=1.0) + + assert sentry_sdk.get_current_scope().root_span is None + + with sentry_sdk.start_span(name="test") as root_span: + assert sentry_sdk.get_current_scope().root_span == root_span + with sentry_sdk.start_span(name="child"): + assert sentry_sdk.get_current_scope().root_span == root_span + with sentry_sdk.start_span(name="grandchild"): + assert sentry_sdk.get_current_scope().root_span == root_span + + assert sentry_sdk.get_current_scope().root_span is None