diff --git a/sentry_sdk/consts.py b/sentry_sdk/consts.py index 1dc6b75958..1f0d0eb240 100644 --- a/sentry_sdk/consts.py +++ b/sentry_sdk/consts.py @@ -26,6 +26,7 @@ "before_send": None, "before_breadcrumb": None, "debug": False, + "attach_stacktrace": True, } diff --git a/sentry_sdk/hub.py b/sentry_sdk/hub.py index f422bb8fb9..685c669dd9 100644 --- a/sentry_sdk/hub.py +++ b/sentry_sdk/hub.py @@ -161,9 +161,7 @@ def capture_exception(self, error=None): else: exc_info = exc_info_from_error(error) - event, hint = event_from_exception( - exc_info, with_locals=client.options["with_locals"] - ) + event, hint = event_from_exception(exc_info, client_options=client.options) try: return self.capture_event(event, hint=hint) except Exception: diff --git a/sentry_sdk/integrations/_wsgi.py b/sentry_sdk/integrations/_wsgi.py index cdfdb2ffc1..9ac8f31fff 100644 --- a/sentry_sdk/integrations/_wsgi.py +++ b/sentry_sdk/integrations/_wsgi.py @@ -169,7 +169,7 @@ def _capture_exception(hub): exc_info = sys.exc_info() event, hint = event_from_exception( exc_info, - with_locals=hub.client.options["with_locals"], + client_options=hub.client.options, mechanism={"type": "wsgi", "handled": False}, ) hub.capture_event(event, hint=hint) diff --git a/sentry_sdk/integrations/aws_lambda.py b/sentry_sdk/integrations/aws_lambda.py index 7099c8ae70..45e7582468 100644 --- a/sentry_sdk/integrations/aws_lambda.py +++ b/sentry_sdk/integrations/aws_lambda.py @@ -41,7 +41,7 @@ def sentry_handler(event, context, *args, **kwargs): exc_info = sys.exc_info() event, hint = event_from_exception( exc_info, - with_locals=hub.client.options["with_locals"], + client_options=hub.client.options, mechanism={"type": "aws_lambda", "handled": False}, ) diff --git a/sentry_sdk/integrations/celery.py b/sentry_sdk/integrations/celery.py index 55b4ce2732..73aee56bf8 100644 --- a/sentry_sdk/integrations/celery.py +++ b/sentry_sdk/integrations/celery.py @@ -45,7 +45,7 @@ def _process_failure_signal(self, sender, task_id, einfo, **kw): def _capture_event(self, hub, exc_info): event, hint = event_from_exception( exc_info, - with_locals=hub.client.options["with_locals"], + client_options=hub.client.options, mechanism={"type": "celery", "handled": False}, ) diff --git a/sentry_sdk/integrations/django/__init__.py b/sentry_sdk/integrations/django/__init__.py index 7d38c50d97..7e8c7330b4 100644 --- a/sentry_sdk/integrations/django/__init__.py +++ b/sentry_sdk/integrations/django/__init__.py @@ -125,7 +125,7 @@ def _got_request_exception(request=None, **kwargs): hub = Hub.current event, hint = event_from_exception( sys.exc_info(), - with_locals=hub.client.options["with_locals"], + client_options=hub.client.options, mechanism={"type": "django", "handled": False}, ) diff --git a/sentry_sdk/integrations/excepthook.py b/sentry_sdk/integrations/excepthook.py index 07b694c23e..93ba2c673a 100644 --- a/sentry_sdk/integrations/excepthook.py +++ b/sentry_sdk/integrations/excepthook.py @@ -23,7 +23,7 @@ def sentry_sdk_excepthook(exctype, value, traceback): hub = Hub.current event, hint = event_from_exception( (exctype, value, traceback), - with_locals=hub.client.options["with_locals"], + client_options=hub.client.options, mechanism={"type": "excepthook", "handled": False}, ) hub.capture_event(event, hint=hint) diff --git a/sentry_sdk/integrations/flask.py b/sentry_sdk/integrations/flask.py index 84edfef43d..b6d0b86815 100644 --- a/sentry_sdk/integrations/flask.py +++ b/sentry_sdk/integrations/flask.py @@ -126,7 +126,7 @@ def _capture_exception(sender, exception, **kwargs): hub = Hub.current event, hint = event_from_exception( exception, - with_locals=hub.client.options["with_locals"], + client_options=hub.client.options, mechanism={"type": "flask", "handled": False}, ) diff --git a/sentry_sdk/integrations/logging.py b/sentry_sdk/integrations/logging.py index 69854e37ad..c42a9aac24 100644 --- a/sentry_sdk/integrations/logging.py +++ b/sentry_sdk/integrations/logging.py @@ -84,7 +84,7 @@ def _emit(self, record): if record.exc_info is not None and record.exc_info[0] is not None: event, hint = event_from_exception( record.exc_info, - with_locals=hub.client.options["with_locals"], + client_options=hub.client.options, mechanism={"type": "logging", "handled": True}, ) else: diff --git a/sentry_sdk/integrations/sanic.py b/sentry_sdk/integrations/sanic.py index 3416a4f27c..b009127a18 100644 --- a/sentry_sdk/integrations/sanic.py +++ b/sentry_sdk/integrations/sanic.py @@ -85,7 +85,7 @@ def _capture_exception(exception): hub = Hub.current event, hint = event_from_exception( exception, - with_locals=hub.client.options["with_locals"], + client_options=hub.client.options, mechanism={"type": "sanic", "handled": False}, ) diff --git a/sentry_sdk/utils.py b/sentry_sdk/utils.py index 8c520f7898..aee33c44fb 100644 --- a/sentry_sdk/utils.py +++ b/sentry_sdk/utils.py @@ -348,7 +348,7 @@ def get_errno(exc_value): def single_exception_from_error_tuple( - exc_type, exc_value, tb, with_locals=True, mechanism=None + exc_type, exc_value, tb, client_options=None, mechanism=None ): errno = get_errno(exc_value) if errno is not None: @@ -356,22 +356,31 @@ def single_exception_from_error_tuple( mechanism_meta = mechanism.setdefault("meta", {}) mechanism_meta.setdefault("errno", {"code": errno}) - return { + rv = { "module": get_type_module(exc_type), "type": get_type_name(exc_type), "value": safe_str(exc_value), - "stacktrace": stacktrace_from_traceback(tb, with_locals), "mechanism": mechanism, } + if client_options is None or client_options["attach_stacktrace"]: + if client_options is None: + with_locals = True + else: + with_locals = client_options["with_locals"] + + rv["stacktrace"] = stacktrace_from_traceback(tb, with_locals) + + return rv + -def exceptions_from_error_tuple(exc_info, with_locals=True, mechanism=None): +def exceptions_from_error_tuple(exc_info, client_options=None, mechanism=None): exc_type, exc_value, tb = exc_info rv = [] while exc_type is not None: rv.append( single_exception_from_error_tuple( - exc_type, exc_value, tb, with_locals, mechanism + exc_type, exc_value, tb, client_options, mechanism ) ) cause = getattr(exc_value, "__cause__", None) @@ -450,14 +459,16 @@ def exc_info_from_error(error): return exc_type, exc_value, tb -def event_from_exception(exc_info, with_locals=False, processors=None, mechanism=None): +def event_from_exception(exc_info, client_options=None, mechanism=None): exc_info = exc_info_from_error(exc_info) hint = event_hint_with_exc_info(exc_info) return ( { "level": "error", "exception": { - "values": exceptions_from_error_tuple(exc_info, with_locals, mechanism) + "values": exceptions_from_error_tuple( + exc_info, client_options, mechanism + ) }, }, hint, diff --git a/tests/test_client.py b/tests/test_client.py index 6322a5bf80..a655cc3f70 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -17,7 +17,7 @@ class EventCaptured(Exception): class _TestTransport(Transport): def capture_event(self, event): - raise EventCaptured() + raise EventCaptured(event) def test_transport_option(monkeypatch): @@ -60,6 +60,64 @@ def e(exc): pytest.raises(EventCaptured, lambda: e(ValueError())) +def test_with_locals_enabled(): + events = [] + hub = Hub(Client(with_locals=True, transport=events.append)) + try: + 1 / 0 + except Exception: + hub.capture_exception() + + event, = events + + assert all( + frame["vars"] + for frame in event["exception"]["values"][0]["stacktrace"]["frames"] + ) + + +def test_with_locals_disabled(): + events = [] + hub = Hub(Client(with_locals=False, transport=events.append)) + try: + 1 / 0 + except Exception: + hub.capture_exception() + + event, = events + + assert all( + "vars" not in frame + for frame in event["exception"]["values"][0]["stacktrace"]["frames"] + ) + + +def test_attach_stacktrace_enabled(): + events = [] + hub = Hub(Client(attach_stacktrace=True, transport=events.append)) + try: + 1 / 0 + except Exception: + hub.capture_exception() + + event, = events + exception, = event["exception"]["values"] + assert exception["stacktrace"]["frames"] + + +def test_attach_stacktrace_disabled(): + events = [] + hub = Hub(Client(attach_stacktrace=False, transport=events.append)) + try: + 1 / 0 + except Exception: + hub.capture_exception() + + event, = events + exception, = event["exception"]["values"] + assert "stacktrace" not in event["exception"]["values"][0] + + def test_capture_event_works(): c = Client(transport=_TestTransport()) pytest.raises(EventCaptured, lambda: c.capture_event({}))