diff --git a/sentry_sdk/integrations/_asgi_common.py b/sentry_sdk/integrations/_asgi_common.py index c16bbbcfe8..ca030d6f45 100644 --- a/sentry_sdk/integrations/_asgi_common.py +++ b/sentry_sdk/integrations/_asgi_common.py @@ -21,7 +21,7 @@ def _get_headers(asgi_scope): Extract headers from the ASGI scope, in the format that the Sentry protocol expects. """ headers = {} # type: Dict[str, str] - for raw_key, raw_value in asgi_scope["headers"]: + for raw_key, raw_value in asgi_scope.get("headers", {}): key = raw_key.decode("latin-1") value = raw_value.decode("latin-1") if key in headers: diff --git a/sentry_sdk/integrations/aiohttp.py b/sentry_sdk/integrations/aiohttp.py index 33f2fc095c..307f71fee3 100644 --- a/sentry_sdk/integrations/aiohttp.py +++ b/sentry_sdk/integrations/aiohttp.py @@ -2,7 +2,6 @@ import weakref import sentry_sdk -from sentry_sdk.api import continue_trace from sentry_sdk.consts import OP, SPANSTATUS, SPANDATA from sentry_sdk.integrations import Integration, DidNotEnable from sentry_sdk.integrations.logging import ignore_logger @@ -113,34 +112,31 @@ async def sentry_app_handle(self, request, *args, **kwargs): scope.add_event_processor(_make_request_processor(weak_request)) headers = dict(request.headers) - transaction = continue_trace( - headers, - op=OP.HTTP_SERVER, - # If this transaction name makes it to the UI, AIOHTTP's - # URL resolver did not find a route or died trying. - name="generic AIOHTTP request", - source=TRANSACTION_SOURCE_ROUTE, - origin=AioHttpIntegration.origin, - ) - with sentry_sdk.start_transaction( - transaction, - custom_sampling_context={"aiohttp_request": request}, - ): - try: - response = await old_handle(self, request) - except HTTPException as e: - transaction.set_http_status(e.status_code) - raise - except (asyncio.CancelledError, ConnectionResetError): - transaction.set_status(SPANSTATUS.CANCELLED) - raise - except Exception: - # This will probably map to a 500 but seems like we - # have no way to tell. Do not set span status. - reraise(*_capture_exception()) - - transaction.set_http_status(response.status) - return response + with sentry_sdk.continue_trace(headers): + with sentry_sdk.start_transaction( + op=OP.HTTP_SERVER, + # If this transaction name makes it to the UI, AIOHTTP's + # URL resolver did not find a route or died trying. + name="generic AIOHTTP request", + source=TRANSACTION_SOURCE_ROUTE, + origin=AioHttpIntegration.origin, + custom_sampling_context={"aiohttp_request": request}, + ) as transaction: + try: + response = await old_handle(self, request) + except HTTPException as e: + transaction.set_http_status(e.status_code) + raise + except (asyncio.CancelledError, ConnectionResetError): + transaction.set_status(SPANSTATUS.CANCELLED) + raise + except Exception: + # This will probably map to a 500 but seems like we + # have no way to tell. Do not set span status. + reraise(*_capture_exception()) + + transaction.set_http_status(response.status) + return response Application._handle = sentry_app_handle diff --git a/sentry_sdk/integrations/asgi.py b/sentry_sdk/integrations/asgi.py index 474a4a40cd..8aac3226d6 100644 --- a/sentry_sdk/integrations/asgi.py +++ b/sentry_sdk/integrations/asgi.py @@ -10,7 +10,6 @@ from functools import partial import sentry_sdk -from sentry_sdk.api import continue_trace from sentry_sdk.consts import OP from sentry_sdk.integrations._asgi_common import ( @@ -34,7 +33,6 @@ transaction_from_function, _get_installed_modules, ) -from sentry_sdk.tracing import Transaction from typing import TYPE_CHECKING @@ -185,66 +183,47 @@ async def _run_app(self, scope, receive, send, asgi_version): scope, ) - if ty in ("http", "websocket"): - transaction = continue_trace( - _get_headers(scope), - op="{}.server".format(ty), + with sentry_sdk.continue_trace(_get_headers(scope)): + with sentry_sdk.start_transaction( + op=( + OP.WEBSOCKET_SERVER + if ty == "websocket" + else OP.HTTP_SERVER + ), name=transaction_name, source=transaction_source, origin=self.span_origin, - ) - logger.debug( - "[ASGI] Created transaction (continuing trace): %s", - transaction, - ) - else: - transaction = Transaction( - op=OP.HTTP_SERVER, - name=transaction_name, - source=transaction_source, - origin=self.span_origin, - ) - logger.debug( - "[ASGI] Created transaction (new): %s", transaction - ) - - transaction.set_tag("asgi.type", ty) - logger.debug( - "[ASGI] Set transaction name and source on transaction: '%s' / '%s'", - transaction.name, - transaction.source, - ) - - with sentry_sdk.start_transaction( - transaction, - custom_sampling_context={"asgi_scope": scope}, - ): - logger.debug("[ASGI] Started transaction: %s", transaction) - try: - - async def _sentry_wrapped_send(event): - # type: (Dict[str, Any]) -> Any - is_http_response = ( - event.get("type") == "http.response.start" - and transaction is not None - and "status" in event - ) - if is_http_response: - transaction.set_http_status(event["status"]) - - return await send(event) - - if asgi_version == 2: - return await self.app(scope)( - receive, _sentry_wrapped_send - ) - else: - return await self.app( - scope, receive, _sentry_wrapped_send + custom_sampling_context={"asgi_scope": scope}, + ) as transaction: + logger.debug("[ASGI] Started transaction: %s", transaction) + transaction.set_tag("asgi.type", ty) + try: + + async def _sentry_wrapped_send(event): + # type: (Dict[str, Any]) -> Any + is_http_response = ( + event.get("type") == "http.response.start" + and transaction is not None + and "status" in event + ) + if is_http_response: + transaction.set_http_status(event["status"]) + + return await send(event) + + if asgi_version == 2: + return await self.app(scope)( + receive, _sentry_wrapped_send + ) + else: + return await self.app( + scope, receive, _sentry_wrapped_send + ) + except Exception as exc: + _capture_exception( + exc, mechanism_type=self.mechanism_type ) - except Exception as exc: - _capture_exception(exc, mechanism_type=self.mechanism_type) - raise exc from None + raise exc from None finally: _asgi_middleware_applied.set(False) diff --git a/sentry_sdk/integrations/aws_lambda.py b/sentry_sdk/integrations/aws_lambda.py index 168b8061aa..a090662608 100644 --- a/sentry_sdk/integrations/aws_lambda.py +++ b/sentry_sdk/integrations/aws_lambda.py @@ -4,7 +4,6 @@ from os import environ import sentry_sdk -from sentry_sdk.api import continue_trace from sentry_sdk.consts import OP from sentry_sdk.scope import should_send_default_pii from sentry_sdk.tracing import TRANSACTION_SOURCE_COMPONENT @@ -135,34 +134,31 @@ def sentry_handler(aws_event, aws_context, *args, **kwargs): if not isinstance(headers, dict): headers = {} - transaction = continue_trace( - headers, - op=OP.FUNCTION_AWS, - name=aws_context.function_name, - source=TRANSACTION_SOURCE_COMPONENT, - origin=AwsLambdaIntegration.origin, - ) - with sentry_sdk.start_transaction( - transaction, - custom_sampling_context={ - "aws_event": aws_event, - "aws_context": aws_context, - }, - ): - try: - return handler(aws_event, aws_context, *args, **kwargs) - except Exception: - exc_info = sys.exc_info() - sentry_event, hint = event_from_exception( - exc_info, - client_options=client.options, - mechanism={"type": "aws_lambda", "handled": False}, - ) - sentry_sdk.capture_event(sentry_event, hint=hint) - reraise(*exc_info) - finally: - if timeout_thread: - timeout_thread.stop() + with sentry_sdk.continue_trace(headers): + with sentry_sdk.start_transaction( + op=OP.FUNCTION_AWS, + name=aws_context.function_name, + source=TRANSACTION_SOURCE_COMPONENT, + origin=AwsLambdaIntegration.origin, + custom_sampling_context={ + "aws_event": aws_event, + "aws_context": aws_context, + }, + ): + try: + return handler(aws_event, aws_context, *args, **kwargs) + except Exception: + exc_info = sys.exc_info() + sentry_event, hint = event_from_exception( + exc_info, + client_options=client.options, + mechanism={"type": "aws_lambda", "handled": False}, + ) + sentry_sdk.capture_event(sentry_event, hint=hint) + reraise(*exc_info) + finally: + if timeout_thread: + timeout_thread.stop() return sentry_handler # type: ignore diff --git a/sentry_sdk/integrations/celery/__init__.py b/sentry_sdk/integrations/celery/__init__.py index 5b8a90fdb9..72483209e3 100644 --- a/sentry_sdk/integrations/celery/__init__.py +++ b/sentry_sdk/integrations/celery/__init__.py @@ -4,7 +4,6 @@ import sentry_sdk from sentry_sdk import isolation_scope -from sentry_sdk.api import continue_trace from sentry_sdk.consts import OP, SPANSTATUS, SPANDATA from sentry_sdk.integrations import Integration, DidNotEnable from sentry_sdk.integrations.celery.beat import ( @@ -301,38 +300,27 @@ def _inner(*args, **kwargs): scope.clear_breadcrumbs() scope.add_event_processor(_make_event_processor(task, *args, **kwargs)) - transaction = None - # Celery task objects are not a thing to be trusted. Even # something such as attribute access can fail. - with capture_internal_exceptions(): - headers = args[3].get("headers") or {} - transaction = continue_trace( - headers, + headers = args[3].get("headers") or {} + with sentry_sdk.continue_trace(headers): + with sentry_sdk.start_transaction( op=OP.QUEUE_TASK_CELERY, - name="unknown celery task", + name=task.name, source=TRANSACTION_SOURCE_TASK, origin=CeleryIntegration.origin, - ) - transaction.name = task.name - transaction.set_status(SPANSTATUS.OK) - - if transaction is None: - return f(*args, **kwargs) - - with sentry_sdk.start_transaction( - transaction, - custom_sampling_context={ - "celery_job": { - "task": task.name, - # for some reason, args[1] is a list if non-empty but a - # tuple if empty - "args": list(args[1]), - "kwargs": args[2], - } - }, - ): - return f(*args, **kwargs) + custom_sampling_context={ + "celery_job": { + "task": task.name, + # for some reason, args[1] is a list if non-empty but a + # tuple if empty + "args": list(args[1]), + "kwargs": args[2], + } + }, + ) as transaction: + transaction.set_status(SPANSTATUS.OK) + return f(*args, **kwargs) return _inner # type: ignore diff --git a/sentry_sdk/integrations/gcp.py b/sentry_sdk/integrations/gcp.py index 688d0de4d4..b977826516 100644 --- a/sentry_sdk/integrations/gcp.py +++ b/sentry_sdk/integrations/gcp.py @@ -4,7 +4,6 @@ from os import environ import sentry_sdk -from sentry_sdk.api import continue_trace from sentry_sdk.consts import OP from sentry_sdk.integrations import Integration from sentry_sdk.integrations._wsgi_common import _filter_headers @@ -82,42 +81,40 @@ def sentry_func(functionhandler, gcp_event, *args, **kwargs): if hasattr(gcp_event, "headers"): headers = gcp_event.headers - transaction = continue_trace( - headers, - op=OP.FUNCTION_GCP, - name=environ.get("FUNCTION_NAME", ""), - source=TRANSACTION_SOURCE_COMPONENT, - origin=GcpIntegration.origin, - ) - sampling_context = { - "gcp_env": { - "function_name": environ.get("FUNCTION_NAME"), - "function_entry_point": environ.get("ENTRY_POINT"), - "function_identity": environ.get("FUNCTION_IDENTITY"), - "function_region": environ.get("FUNCTION_REGION"), - "function_project": environ.get("GCP_PROJECT"), - }, - "gcp_event": gcp_event, - } - with sentry_sdk.start_transaction( - transaction, custom_sampling_context=sampling_context - ): - try: - return func(functionhandler, gcp_event, *args, **kwargs) - except Exception: - exc_info = sys.exc_info() - sentry_event, hint = event_from_exception( - exc_info, - client_options=client.options, - mechanism={"type": "gcp", "handled": False}, - ) - sentry_sdk.capture_event(sentry_event, hint=hint) - reraise(*exc_info) - finally: - if timeout_thread: - timeout_thread.stop() - # Flush out the event queue - client.flush() + with sentry_sdk.continue_trace(headers): + sampling_context = { + "gcp_env": { + "function_name": environ.get("FUNCTION_NAME"), + "function_entry_point": environ.get("ENTRY_POINT"), + "function_identity": environ.get("FUNCTION_IDENTITY"), + "function_region": environ.get("FUNCTION_REGION"), + "function_project": environ.get("GCP_PROJECT"), + }, + "gcp_event": gcp_event, + } + with sentry_sdk.start_transaction( + op=OP.FUNCTION_GCP, + name=environ.get("FUNCTION_NAME", ""), + source=TRANSACTION_SOURCE_COMPONENT, + origin=GcpIntegration.origin, + custom_sampling_context=sampling_context, + ): + try: + return func(functionhandler, gcp_event, *args, **kwargs) + except Exception: + exc_info = sys.exc_info() + sentry_event, hint = event_from_exception( + exc_info, + client_options=client.options, + mechanism={"type": "gcp", "handled": False}, + ) + sentry_sdk.capture_event(sentry_event, hint=hint) + reraise(*exc_info) + finally: + if timeout_thread: + timeout_thread.stop() + # Flush out the event queue + client.flush() return sentry_func # type: ignore diff --git a/sentry_sdk/integrations/huey.py b/sentry_sdk/integrations/huey.py index 98fab46711..7aa3cbf490 100644 --- a/sentry_sdk/integrations/huey.py +++ b/sentry_sdk/integrations/huey.py @@ -2,7 +2,7 @@ from datetime import datetime import sentry_sdk -from sentry_sdk.api import continue_trace, get_baggage, get_traceparent +from sentry_sdk.api import get_baggage, get_traceparent from sentry_sdk.consts import OP, SPANSTATUS from sentry_sdk.integrations import DidNotEnable, Integration from sentry_sdk.scope import should_send_default_pii @@ -153,22 +153,19 @@ def _sentry_execute(self, task, timestamp=None): scope.clear_breadcrumbs() scope.add_event_processor(_make_event_processor(task)) - sentry_headers = task.kwargs.pop("sentry_headers", None) - - transaction = continue_trace( - sentry_headers or {}, - name=task.name, - op=OP.QUEUE_TASK_HUEY, - source=TRANSACTION_SOURCE_TASK, - origin=HueyIntegration.origin, - ) - transaction.set_status(SPANSTATUS.OK) - if not getattr(task, "_sentry_is_patched", False): task.execute = _wrap_task_execute(task.execute) task._sentry_is_patched = True - with sentry_sdk.start_transaction(transaction): - return old_execute(self, task, timestamp) + sentry_headers = task.kwargs.pop("sentry_headers", {}) + with sentry_sdk.continue_trace(sentry_headers): + with sentry_sdk.start_transaction( + name=task.name, + op=OP.QUEUE_TASK_HUEY, + source=TRANSACTION_SOURCE_TASK, + origin=HueyIntegration.origin, + ) as transaction: + transaction.set_status(SPANSTATUS.OK) + return old_execute(self, task, timestamp) Huey._execute = _sentry_execute diff --git a/sentry_sdk/integrations/ray.py b/sentry_sdk/integrations/ray.py index bafd42c8d6..b18017ee7f 100644 --- a/sentry_sdk/integrations/ray.py +++ b/sentry_sdk/integrations/ray.py @@ -58,25 +58,23 @@ def _f(*f_args, _tracing=None, **f_kwargs): """ _check_sentry_initialized() - transaction = sentry_sdk.continue_trace( - _tracing or {}, - op=OP.QUEUE_TASK_RAY, - name=qualname_from_function(f), - origin=RayIntegration.origin, - source=TRANSACTION_SOURCE_TASK, - ) - - with sentry_sdk.start_transaction(transaction) as transaction: - try: - result = f(*f_args, **f_kwargs) - transaction.set_status(SPANSTATUS.OK) - except Exception: - transaction.set_status(SPANSTATUS.INTERNAL_ERROR) - exc_info = sys.exc_info() - _capture_exception(exc_info) - reraise(*exc_info) - - return result + with sentry_sdk.continue_trace(_tracing or {}): + with sentry_sdk.start_transaction( + op=OP.QUEUE_TASK_RAY, + name=qualname_from_function(f) or "unknown Ray function", + origin=RayIntegration.origin, + source=TRANSACTION_SOURCE_TASK, + ) as transaction: + try: + result = f(*f_args, **f_kwargs) + transaction.set_status(SPANSTATUS.OK) + except Exception: + transaction.set_status(SPANSTATUS.INTERNAL_ERROR) + exc_info = sys.exc_info() + _capture_exception(exc_info) + reraise(*exc_info) + + return result rv = old_remote(_f, *args, *kwargs) old_remote_method = rv.remote diff --git a/sentry_sdk/integrations/rq.py b/sentry_sdk/integrations/rq.py index c0df1c5e53..7e84b15681 100644 --- a/sentry_sdk/integrations/rq.py +++ b/sentry_sdk/integrations/rq.py @@ -2,7 +2,6 @@ import sentry_sdk from sentry_sdk.consts import OP -from sentry_sdk.api import continue_trace from sentry_sdk.integrations import DidNotEnable, Integration from sentry_sdk.integrations.logging import ignore_logger from sentry_sdk.tracing import TRANSACTION_SOURCE_TASK @@ -59,22 +58,20 @@ def sentry_patched_perform_job(self, job, *args, **kwargs): scope.clear_breadcrumbs() scope.add_event_processor(_make_event_processor(weakref.ref(job))) - transaction = continue_trace( - job.meta.get("_sentry_trace_headers") or {}, - op=OP.QUEUE_TASK_RQ, - name="unknown RQ task", - source=TRANSACTION_SOURCE_TASK, - origin=RqIntegration.origin, - ) - - with capture_internal_exceptions(): - transaction.name = job.func_name - - with sentry_sdk.start_transaction( - transaction, - custom_sampling_context={"rq_job": job}, + with sentry_sdk.continue_trace( + job.meta.get("_sentry_trace_headers") or {} ): - rv = old_perform_job(self, job, *args, **kwargs) + with sentry_sdk.start_transaction( + op=OP.QUEUE_TASK_RQ, + name="unknown RQ task", + source=TRANSACTION_SOURCE_TASK, + origin=RqIntegration.origin, + custom_sampling_context={"rq_job": job}, + ) as transaction: + with capture_internal_exceptions(): + transaction.name = job.func_name + + rv = old_perform_job(self, job, *args, **kwargs) if self.is_horse: # We're inside of a forked process and RQ is diff --git a/sentry_sdk/integrations/sanic.py b/sentry_sdk/integrations/sanic.py index 408216329f..3dbc556557 100644 --- a/sentry_sdk/integrations/sanic.py +++ b/sentry_sdk/integrations/sanic.py @@ -4,7 +4,6 @@ from urllib.parse import urlsplit import sentry_sdk -from sentry_sdk import continue_trace from sentry_sdk.consts import OP from sentry_sdk.integrations import Integration, DidNotEnable from sentry_sdk.integrations._wsgi_common import RequestExtractor, _filter_headers @@ -193,16 +192,17 @@ async def _context_enter(request): scope.clear_breadcrumbs() scope.add_event_processor(_make_request_processor(weak_request)) - transaction = continue_trace( - dict(request.headers), + # TODO-neel-potel test if this works + request.ctx._sentry_continue_trace = sentry_sdk.continue_trace( + dict(request.headers) + ) + request.ctx._sentry_continue_trace.__enter__() + request.ctx._sentry_transaction = sentry_sdk.start_transaction( op=OP.HTTP_SERVER, # Unless the request results in a 404 error, the name and source will get overwritten in _set_transaction name=request.path, source=TRANSACTION_SOURCE_URL, origin=SanicIntegration.origin, - ) - request.ctx._sentry_transaction = sentry_sdk.start_transaction( - transaction ).__enter__() @@ -227,6 +227,7 @@ async def _context_exit(request, response=None): and response_status not in integration._unsampled_statuses ) request.ctx._sentry_transaction.__exit__(None, None, None) + request.ctx._sentry_continue_trace.__exit__(None, None, None) request.ctx._sentry_scope.__exit__(None, None, None) diff --git a/sentry_sdk/integrations/tornado.py b/sentry_sdk/integrations/tornado.py index f1bd196261..21532fbba5 100644 --- a/sentry_sdk/integrations/tornado.py +++ b/sentry_sdk/integrations/tornado.py @@ -3,7 +3,6 @@ from inspect import iscoroutinefunction import sentry_sdk -from sentry_sdk.api import continue_trace from sentry_sdk.consts import OP from sentry_sdk.scope import should_send_default_pii from sentry_sdk.tracing import ( @@ -115,22 +114,19 @@ def _handle_request_impl(self): processor = _make_event_processor(weak_handler) scope.add_event_processor(processor) - transaction = continue_trace( - headers, - op=OP.HTTP_SERVER, - # Like with all other integrations, this is our - # fallback transaction in case there is no route. - # sentry_urldispatcher_resolve is responsible for - # setting a transaction name later. - name="generic Tornado request", - source=TRANSACTION_SOURCE_ROUTE, - origin=TornadoIntegration.origin, - ) - - with sentry_sdk.start_transaction( - transaction, custom_sampling_context={"tornado_request": self.request} - ): - yield + with sentry_sdk.continue_trace(headers): + with sentry_sdk.start_transaction( + op=OP.HTTP_SERVER, + # Like with all other integrations, this is our + # fallback transaction in case there is no route. + # sentry_urldispatcher_resolve is responsible for + # setting a transaction name later. + name="generic Tornado request", + source=TRANSACTION_SOURCE_ROUTE, + origin=TornadoIntegration.origin, + custom_sampling_context={"tornado_request": self.request}, + ): + yield @ensure_integration_enabled(TornadoIntegration) diff --git a/sentry_sdk/integrations/wsgi.py b/sentry_sdk/integrations/wsgi.py index a9bc5cd90a..bfd303235e 100644 --- a/sentry_sdk/integrations/wsgi.py +++ b/sentry_sdk/integrations/wsgi.py @@ -3,7 +3,6 @@ import sentry_sdk from sentry_sdk._werkzeug import get_host, _get_headers -from sentry_sdk.api import continue_trace from sentry_sdk.consts import OP from sentry_sdk.scope import should_send_default_pii from sentry_sdk.integrations._wsgi_common import _filter_headers @@ -92,7 +91,7 @@ def __call__(self, environ, start_response): ) ) - with continue_trace(environ): + with sentry_sdk.continue_trace(environ): with sentry_sdk.start_transaction( op=OP.HTTP_SERVER, name="generic WSGI request",