Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 20 additions & 13 deletions sentry_sdk/integrations/_wsgi.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import json
import sys

from sentry_sdk import capture_exception
from sentry_sdk.hub import Hub, _should_send_default_pii, _get_client_options
from sentry_sdk.utils import AnnotatedValue, capture_internal_exceptions
from sentry_sdk.utils import (
AnnotatedValue,
capture_internal_exceptions,
event_from_exception,
)
from sentry_sdk._compat import reraise, implements_iterator


Expand Down Expand Up @@ -155,14 +158,24 @@ def run_wsgi_app(app, environ, start_response):
try:
rv = app(environ, start_response)
except Exception:
einfo = sys.exc_info()
capture_exception(einfo)
einfo = _capture_exception(hub)
hub.pop_scope_unsafe()
reraise(*einfo)

return _ScopePoppingResponse(hub, rv)


def _capture_exception(hub):
exc_info = sys.exc_info()
event, hint = event_from_exception(
exc_info,
with_locals=hub.client.options["with_locals"],
mechanism={"type": "wsgi", "handled": False},
)
hub.capture_event(event, hint=hint)
return exc_info


@implements_iterator
class _ScopePoppingResponse(object):
__slots__ = ("_response", "_hub")
Expand All @@ -175,9 +188,7 @@ def __iter__(self):
try:
self._response = iter(self._response)
except Exception:
einfo = sys.exc_info()
capture_exception(einfo)
reraise(*einfo)
reraise(*_capture_exception(self.hub))
return self

def __next__(self):
Expand All @@ -186,19 +197,15 @@ def __next__(self):
except StopIteration:
raise
except Exception:
einfo = sys.exc_info()
capture_exception(einfo)
reraise(*einfo)
reraise(*_capture_exception(self.hub))

def close(self):
self._hub.pop_scope_unsafe()
if hasattr(self._response, "close"):
try:
self._response.close()
except Exception:
einfo = sys.exc_info()
capture_exception(einfo)
reraise(*einfo)
reraise(*_capture_exception(self.hub))


def _make_wsgi_event_processor(environ):
Expand Down
15 changes: 12 additions & 3 deletions sentry_sdk/integrations/celery.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from celery.exceptions import SoftTimeLimitExceeded

from sentry_sdk import Hub
from sentry_sdk.utils import capture_internal_exceptions
from sentry_sdk.utils import capture_internal_exceptions, event_from_exception
from sentry_sdk.integrations import Integration


Expand Down Expand Up @@ -33,9 +33,18 @@ def _process_failure_signal(self, sender, task_id, einfo, **kw):
getattr(sender, "name", sender),
]

hub.capture_exception(einfo.exc_info)
self._capture_event(hub, einfo.exc_info)
else:
hub.capture_exception(einfo.exc_info)
self._capture_event(hub, einfo.exc_info)

def _capture_event(self, hub, exc_info):
event, hint = event_from_exception(
exc_info,
with_locals=hub.client.options["with_locals"],
mechanism={"type": "celery", "handled": False},
)

hub.capture_event(event, hint=hint)

def _handle_task_prerun(self, sender, task, **kw):
with capture_internal_exceptions():
Expand Down
14 changes: 11 additions & 3 deletions sentry_sdk/integrations/django.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import absolute_import

import sys
import weakref

from django import VERSION as DJANGO_VERSION
Expand All @@ -10,9 +11,9 @@
except ImportError:
from django.core.urlresolvers import resolve

from sentry_sdk import capture_exception, configure_scope
from sentry_sdk import Hub, configure_scope
from sentry_sdk.hub import _should_send_default_pii
from sentry_sdk.utils import capture_internal_exceptions
from sentry_sdk.utils import capture_internal_exceptions, event_from_exception
from sentry_sdk.integrations import Integration
from sentry_sdk.integrations._wsgi import RequestExtractor, run_wsgi_app

Expand Down Expand Up @@ -90,7 +91,14 @@ def event_processor(event, hint):


def _got_request_exception(request=None, **kwargs):
capture_exception()
hub = Hub.current
event, hint = event_from_exception(
sys.exc_info(),
with_locals=hub.client.options["with_locals"],
mechanism={"type": "django", "handled": False},
)

hub.capture_event(event, hint=hint)


class DjangoRequestExtractor(RequestExtractor):
Expand Down
12 changes: 9 additions & 3 deletions sentry_sdk/integrations/excepthook.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import sys

from sentry_sdk import capture_exception
from sentry_sdk.utils import capture_internal_exceptions
from sentry_sdk import Hub
from sentry_sdk.utils import capture_internal_exceptions, event_from_exception
from sentry_sdk.integrations import Integration


Expand All @@ -20,7 +20,13 @@ def install(self):
def _make_excepthook(old_excepthook):
def sentry_sdk_excepthook(exctype, value, traceback):
with capture_internal_exceptions():
capture_exception((exctype, value, traceback))
hub = Hub.current
event, hint = event_from_exception(
(exctype, value, traceback),
with_locals=hub.client.options["with_locals"],
mechanism={"type": "excepthook", "handled": False},
)
hub.capture_event(event, hint=hint)

return old_excepthook(exctype, value, traceback)

Expand Down
13 changes: 10 additions & 3 deletions sentry_sdk/integrations/flask.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

import weakref

from sentry_sdk import Hub, capture_exception, configure_scope
from sentry_sdk import Hub, configure_scope
from sentry_sdk.hub import _should_send_default_pii
from sentry_sdk.utils import capture_internal_exceptions
from sentry_sdk.utils import capture_internal_exceptions, event_from_exception
from sentry_sdk.integrations import Integration
from sentry_sdk.integrations._wsgi import RequestExtractor, run_wsgi_app

Expand Down Expand Up @@ -105,7 +105,14 @@ def size_of_file(self, file):


def _capture_exception(sender, exception, **kwargs):
capture_exception(exception)
hub = Hub.current
event, hint = event_from_exception(
exception,
with_locals=hub.client.options["with_locals"],
mechanism={"type": "flask", "handled": False},
)

hub.capture_event(event, hint=hint)


def _make_request_event_processor(app, weak_request):
Expand Down
17 changes: 9 additions & 8 deletions sentry_sdk/integrations/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import logging
import datetime

from sentry_sdk import capture_event, add_breadcrumb
from sentry_sdk.utils import (
to_string,
event_from_exception,
Expand Down Expand Up @@ -72,17 +71,19 @@ def _emit(self, record):
if not self.can_record(record):
return

if self._should_create_event(record):
hub = Hub.current
if hub.client is None:
return
hub = Hub.current
if hub.client is None:
return

if self._should_create_event(record):
with capture_internal_exceptions():
hint = None
# exc_info might be None or (None, None, None)
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"]
record.exc_info,
with_locals=hub.client.options["with_locals"],
mechanism={"type": "logging", "handled": True},
)
else:
event = {}
Expand All @@ -94,10 +95,10 @@ def _emit(self, record):
"params": record.args,
}

capture_event(event, hint=hint)
hub.capture_event(event, hint=hint)

with capture_internal_exceptions():
add_breadcrumb(
hub.add_breadcrumb(
self._breadcrumb_from_record(record), hint={"log_record": record}
)

Expand Down
27 changes: 22 additions & 5 deletions sentry_sdk/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,21 +322,36 @@ def stacktrace_from_traceback(tb, with_locals=True):
return {"frames": [frame_from_traceback(tb, with_locals) for tb in iter_stacks(tb)]}


def single_exception_from_error_tuple(exc_type, exc_value, tb, with_locals=True):
def get_errno(exc_value):
return getattr(exc_value, "errno", None)


def single_exception_from_error_tuple(
exc_type, exc_value, tb, with_locals=True, mechanism=None
):
errno = get_errno(exc_value)
if errno is not None:
mechanism = mechanism or {}
mechanism_meta = mechanism.setdefault("meta", {})
mechanism_meta.setdefault("errno", {"code": errno})

return {
"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,
}


def exceptions_from_error_tuple(exc_info, with_locals=True):
def exceptions_from_error_tuple(exc_info, with_locals=True, 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)
single_exception_from_error_tuple(
exc_type, exc_value, tb, with_locals, mechanism
)
)
cause = getattr(exc_value, "__cause__", None)
if cause is None:
Expand Down Expand Up @@ -414,13 +429,15 @@ def exc_info_from_error(error):
return exc_type, exc_value, tb


def event_from_exception(exc_info, with_locals=False, processors=None):
def event_from_exception(exc_info, with_locals=False, processors=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)},
"exception": {
"values": exceptions_from_error_tuple(exc_info, with_locals, mechanism)
},
},
hint,
)
Expand Down
4 changes: 3 additions & 1 deletion tests/integrations/celery/test_celery.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ def celery(sentry_init):


def test_simple(capture_events, celery):

events = capture_events()

@celery.task(name="dummy_task")
Expand All @@ -31,6 +30,9 @@ def dummy_task(x, y):
exception, = event["exception"]["values"]
assert exception["type"] == "ZeroDivisionError"

event, = events
assert event["exception"]["values"][0]["mechanism"]["type"] == "celery"


def test_ignore_expected(capture_events, celery):
events = capture_events()
Expand Down
20 changes: 8 additions & 12 deletions tests/integrations/conftest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import sys
import pytest
import sentry_sdk

Expand All @@ -7,19 +6,16 @@
def capture_exceptions(monkeypatch):
def inner():
errors = set()
old_capture_exception = sentry_sdk.Hub.current.capture_exception
old_capture_event = sentry_sdk.Hub.current.capture_event

def capture_exception(error=None):
given_error = error
error = error or sys.exc_info()[1]
if isinstance(error, tuple):
error = error[1]
errors.add(error)
return old_capture_exception(given_error)
def capture_event(event, hint=None):
if hint:
if "exc_info" in hint:
error = hint["exc_info"][1]
errors.add(error)
return old_capture_event(event, hint=hint)

monkeypatch.setattr(
sentry_sdk.Hub.current, "capture_exception", capture_exception
)
monkeypatch.setattr(sentry_sdk.Hub.current, "capture_event", capture_event)
return errors

return inner
6 changes: 5 additions & 1 deletion tests/integrations/django/test_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,17 @@ def client(monkeypatch_test_transport):
return Client(application)


def test_view_exceptions(client, capture_exceptions):
def test_view_exceptions(client, capture_exceptions, capture_events):
exceptions = capture_exceptions()
events = capture_events()
client.get(reverse("view_exc"))

error, = exceptions
assert isinstance(error, ZeroDivisionError)

event, = events
assert event["exception"]["values"][0]["mechanism"]["type"] == "django"


def test_middleware_exceptions(client, capture_exceptions):
exceptions = capture_exceptions()
Expand Down
Loading