Skip to content

Commit b3a8996

Browse files
authored
[wip] feat: basic logging integration (#4)
feat: basic logging integration
2 parents 77e39fd + 3b52035 commit b3a8996

File tree

6 files changed

+96
-2
lines changed

6 files changed

+96
-2
lines changed

sentry_sdk/hub.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,11 @@ def capture_exception(self, error=None):
133133
}
134134
return self.capture_event(event)
135135

136+
def capture_internal_exception(self, error=None):
137+
"""Capture an exception that is likely caused by a bug in the SDK
138+
itself."""
139+
pass
140+
136141
def add_breadcrumb(self, crumb):
137142
"""Adds a breadcrumb."""
138143
client, scope = self._stack[-1]

sentry_sdk/integrations/logging.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
from __future__ import absolute_import
2+
from __future__ import print_function
3+
4+
import logging
5+
6+
from sentry_sdk import get_current_hub, capture_event
7+
from sentry_sdk.utils import to_string, create_event, exceptions_from_error_tuple, skip_internal_frames
8+
9+
class SentryHandler(logging.Handler, object):
10+
def emit(self, record):
11+
try:
12+
self.format(record)
13+
return self._emit(record)
14+
except Exception:
15+
get_current_hub().capture_internal_exception()
16+
17+
def can_record(self, record):
18+
return not record.name.startswith('sentry_sdk')
19+
20+
def _emit(self, record):
21+
if not self.can_record(record):
22+
print(to_string(record.message), file=sys.stderr)
23+
return
24+
25+
event = create_event()
26+
27+
# exc_info might be None or (None, None, None)
28+
if record.exc_info and all(record.exc_info):
29+
exc_type, exc_value, tb = record.exc_info
30+
event['exception'] = exceptions_from_error_tuple(
31+
exc_type, exc_value, skip_internal_frames(tb),
32+
get_current_hub().client.options['with_locals']
33+
)
34+
35+
event['level'] = record.levelname.lower()
36+
event['logger'] = record.name
37+
38+
event['logentry'] = {
39+
'message': to_string(record.msg),
40+
'params': record.args
41+
}
42+
43+
capture_event(event)
44+
45+
46+
HANDLER = SentryHandler()

sentry_sdk/utils.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,3 +332,10 @@ def create_event():
332332
'timestamp': datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ'),
333333
'level': 'error',
334334
}
335+
336+
337+
def to_string(value):
338+
try:
339+
return text_type(value)
340+
except UnicodeDecodeError:
341+
return repr(value)[1:-1]

tests/conftest.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import pytest
2+
import sentry_sdk
3+
sentry_sdk.init()
4+
5+
6+
@pytest.fixture
7+
def capture_exceptions(monkeypatch):
8+
errors = []
9+
def capture_exception(error=None):
10+
errors.append(error or sys.exc_info()[1])
11+
12+
monkeypatch.setattr(sentry_sdk.Hub.current,
13+
'capture_exception', capture_exception)
14+
return errors
15+
16+
17+
@pytest.fixture
18+
def capture_events(monkeypatch):
19+
events = []
20+
monkeypatch.setattr(sentry_sdk.Hub.current, 'capture_event', events.append)
21+
return events

tests/django/myapp/views.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33

44
import sentry_sdk
55

6-
sentry_sdk.init()
7-
86
def self_check(request):
97
with sentry_sdk.configure_scope() as scope:
108
assert scope._data['transaction'] == self_check

tests/logging/test_logging.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import pytest
2+
import logging
3+
4+
from sentry_sdk.integrations.logging import HANDLER
5+
6+
logger = logging.getLogger(__name__)
7+
8+
logger.handlers = [HANDLER]
9+
logger.setLevel(logging.DEBUG)
10+
11+
@pytest.mark.parametrize('level', ['info', 'debug', 'warning', 'error'])
12+
def test_logging(capture_events, level):
13+
getattr(logger, level)('LOL')
14+
event, = capture_events
15+
assert event['level'] == level
16+
assert not event['logentry']['params']
17+
assert event['logentry']['message'] == 'LOL'

0 commit comments

Comments
 (0)