Skip to content

Commit

Permalink
feat: Added unified hint api (#61)
Browse files Browse the repository at this point in the history
  • Loading branch information
mitsuhiko authored Sep 13, 2018
1 parent 3140d17 commit 3aa65d5
Show file tree
Hide file tree
Showing 8 changed files with 56 additions and 34 deletions.
3 changes: 1 addition & 2 deletions sentry_sdk/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@

from sentry_sdk.hub import Hub
from sentry_sdk.scope import Scope
from sentry_sdk.utils import EventHint
from sentry_sdk.transport import Transport, HttpTransport
from sentry_sdk.client import Client, get_options
from sentry_sdk.integrations import setup_integrations


__all__ = ["Hub", "Scope", "Client", "EventHint", "Transport", "HttpTransport"]
__all__ = ["Hub", "Scope", "Client", "Transport", "HttpTransport"]


def public(f):
Expand Down
9 changes: 6 additions & 3 deletions sentry_sdk/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ def _prepare_event(self, event, hint, scope):
return event

def _is_ignored_error(self, event, hint=None):
exc_info = hint and hint.exc_info or None
exc_info = hint.get("exc_info")
if exc_info is None:
return False

Expand All @@ -123,7 +123,7 @@ def _is_ignored_error(self, event, hint=None):

return False

def _should_capture(self, event, hint=None, scope=None):
def _should_capture(self, event, hint, scope=None):
if (
self.options["sample_rate"] < 1.0
and random.random() >= self.options["sample_rate"]
Expand All @@ -140,13 +140,16 @@ def capture_event(self, event, hint=None, scope=None):
This takes the ready made event and an optoinal hint and scope. The
hint is internally used to further customize the representation of the
error. For more information see `EventHint`.
error. When provided it's a dictionary of optional information such
as exception info.
If the transport is not set nothing happens, otherwise the return
value of this function will be the ID of the captured event.
"""
if self.transport is None:
return
if hint is None:
hint = {}
rv = event.get("event_id")
if rv is None:
event["event_id"] = rv = uuid.uuid4().hex
Expand Down
2 changes: 1 addition & 1 deletion sentry_sdk/hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def capture_event(self, event, hint=None):
"""Captures an event. The return value is the ID of the event.
The event is a dictionary following the Sentry v7/v8 protocol
specification. Optionally an `EventHint` object can be passed that
specification. Optionally an event hint dict can be passed that
is used by processors to extract additional information from it.
Typically the event hint object would contain exception information.
"""
Expand Down
7 changes: 6 additions & 1 deletion sentry_sdk/integrations/stdlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@ def getresponse(self, *args, **kwargs):
if "status_code" not in data:
data["status_code"] = rv.status
data["reason"] = rv.reason
add_breadcrumb(type="http", category="httplib", data=data)
add_breadcrumb(
type="http",
category="httplib",
data=data,
hint={"httplib_response": rv},
)
return rv

self.httplib_connection_cls.putrequest = putrequest
Expand Down
4 changes: 2 additions & 2 deletions sentry_sdk/scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,8 @@ def _drop(event, cause, ty):
if self._contexts:
event.setdefault("contexts", {}).update(self._contexts)

if hint is not None and hint.exc_info is not None:
exc_info = hint.exc_info
exc_info = hint.get("exc_info") if hint is not None else None
if exc_info is not None:
for processor in self._error_processors:
new_event = processor(event, exc_info)
if new_event is None:
Expand Down
33 changes: 10 additions & 23 deletions sentry_sdk/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,28 +36,15 @@ def to_timestamp(value):
return (value - epoch).total_seconds()


class EventHint(object):
"""Extra information for an event that can be used during processing."""

def __init__(self, exc_info=None):
self.exc_info = exc_info

@property
def exception(self):
"""Returns the exception value on the hint if there is one."""
if self.exc_info is not None:
return self.exc_info[1]

@classmethod
def with_exc_info(cls, exc_info=None):
"""Creates a hint with the exc info filled in."""
if exc_info is None:
exc_info = sys.exc_info()
else:
exc_info = exc_info_from_error(exc_info)
if exc_info[0] is None:
exc_info = None
return cls(exc_info=exc_info)
def event_hint_with_exc_info(exc_info=None):
"""Creates a hint with the exc info filled in."""
if exc_info is None:
exc_info = sys.exc_info()
else:
exc_info = exc_info_from_error(exc_info)
if exc_info[0] is None:
exc_info = None
return {"exc_info": exc_info}


class BadDsn(ValueError):
Expand Down Expand Up @@ -429,7 +416,7 @@ def exc_info_from_error(error):

def event_from_exception(exc_info, with_locals=False, processors=None):
exc_info = exc_info_from_error(exc_info)
hint = EventHint.with_exc_info(exc_info)
hint = event_hint_with_exc_info(exc_info)
return (
{
"level": "error",
Expand Down
29 changes: 29 additions & 0 deletions tests/integrations/stdlib/test_httplib.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,32 @@ def test_crumb_capture(sentry_init, capture_events):
"status_code": 200,
"reason": "OK",
}


def test_crumb_capture_hint(sentry_init, capture_events):
def before_breadcrumb(crumb, hint):
if "httplib_response" in hint:
con = hint["httplib_response"].getheader("Connection")
assert con.lower() == "close"
crumb["data"]["extra"] = "foo"
return crumb

sentry_init(integrations=[StdlibIntegration()], before_breadcrumb=before_breadcrumb)
events = capture_events()

url = "https://httpbin.org/status/200"
response = urlopen(url)
assert response.getcode() == 200
capture_message("Testing!")

event, = events
crumb, = event["breadcrumbs"]
assert crumb["type"] == "http"
assert crumb["category"] == "httplib"
assert crumb["data"] == {
"url": url,
"method": "GET",
"status_code": 200,
"reason": "OK",
"extra": "foo",
}
3 changes: 1 addition & 2 deletions tests/test_basics.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ def test_option_callback(sentry_init, capture_events):
drop_breadcrumbs = False

def before_send(event, hint):
assert hint is not None
assert isinstance(hint.exception, ValueError)
assert isinstance(hint["exc_info"][1], ValueError)
if not drop_events:
event["extra"] = {"foo": "bar"}
return event
Expand Down

0 comments on commit 3aa65d5

Please sign in to comment.