8000 feat: Threading integration (#258) · etherscan-io/sentry-python@28133ca · GitHub
[go: up one dir, main page]

Skip to content

Commit 28133ca

Browse files
authored
feat: Threading integration (getsentry#258)
* feat: Threading integration * fix: Import cycle
1 parent edbd748 commit 28133ca

File tree

3 files changed

+132
-0
lines changed

3 files changed

+132
-0
lines changed

sentry_sdk/integrations/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
"""This package"""
2+
from __future__ import absolute_import
3+
24
from threading import Lock
35
from collections import namedtuple
46

@@ -30,6 +32,7 @@ def iter_default_integrations():
3032
from sentry_sdk.integrations.atexit import AtexitIntegration
3133
from sentry_sdk.integrations.modules import ModulesIntegration
3234
from sentry_sdk.integrations.argv import ArgvIntegration
35+
from sentry_sdk.integrations.threading import ThreadingIntegration
3336

3437
yield LoggingIntegration
3538
yield StdlibIntegration
@@ -38,6 +41,7 @@ def iter_default_integrations():
3841
yield AtexitIntegration
3942
yield ModulesIntegration
4043
yield ArgvIntegration
44+
yield ThreadingIntegration
4145

4246

4347
def setup_integrations(integrations, with_defaults=True):

sentry_sdk/integrations/threading.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
from __future__ import absolute_import
2+
3+
import sys
4+
5+
from threading import Thread
6+
7+
from sentry_sdk import Hub
8+
from sentry_sdk._compat import reraise
9+
from sentry_sdk.utils import event_from_exception
10+
from sentry_sdk.integrations import Integration
11+
12+
13+
class ThreadingIntegration(Integration):
14+
identifier = "threading"
15+
16+
def __init__(self, propagate_hub=False):
17+
self.propagate_hub = propagate_hub
18+
19+
@staticmethod
20+
def setup_once():
21+
old_start = Thread.start
22+
23+
def sentry_start(self, *a, **kw):
24+
hub = Hub.current
25+
integration = hub.get_integration(ThreadingIntegration)
26+
if integration is not None:
27+
if integration.propagate_hub:
28+
hub = Hub(hub)
29+
else:
30+
hub = None
31+
32+
self.run = _wrap_run(hub, self.run)
33+
34+
return old_start(self, *a, **kw)
35+
36+
Thread.start = sentry_start
37+
38+
39+
def _wrap_run(parent_hub, old_run):
40+
def run(*a, **kw):
41+
hub = parent_hub or Hub.current
42+
43+
with hub:
44+
try:
45+
return old_run(*a, **kw)
46+
except Exception:
47+
reraise(*_capture_exception())
48+
49+
return run
50+
51+
52+
def _capture_exception():
53+
hub = Hub.current
54+
exc_info = sys.exc_info()
55+
56+
if hub. 8000 get_integration(ThreadingIntegration) is not None:
57+
event, hint = event_from_exception(
58+
exc_info,
59+
client_options=hub.client.options,
60+
mechanism={"type": "threading", "handled": False},
61+
)
62+
hub.capture_event(event, hint=hint)
63+
64+
return exc_info
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
from threading import Thread
2+
3+
import pytest
4+
5+
from sentry_sdk import configure_scope
6+
from sentry_sdk.integrations.threading import ThreadingIntegration
7+
8+
9+
@pytest.mark.parametrize("integrations", [[ThreadingIntegration()], []])
10+
def test_handles_exceptions(sentry_init, capture_events, integrations):
11+
sentry_init(default_integrations=False, integrations=integrations)
12+
events = capture_events()
13+
14+
def crash():
15+
1 / 0
16+
17+
t = Thread(target=crash)
18+
t.start()
19+
t.join()
20+
21+
if integrations:
22+
event, = events
23+
24+
exception, = event["exception"]["values"]
25+
assert exception["type"] == "ZeroDivisionError"
26+
assert exception["mechanism"] == {"type": "threading", "handled": False}
27+
else:
28+
assert not events
29+
30+
31+
@pytest.mark.parametrize("propagate_hub", (True, False))
32+
def test_propagates_hub(sentry_init, capture_events, propagate_hub):
33+
sentry_init(
34+
default_integrations=False,
35+
integrations=[ThreadingIntegration(propagate_hub=propagate_hub)],
36+
)
37+
events = capture_events()
38+
39+
def stage1():
40+
with configure_scope() as scope:
41+
scope.set_tag("stage1", True)
42+
43+
t = Thread(target=stage2)
44+
t.start()
45+
t.join()
46+
47+
def stage2():
48+
1 / 0
49+
50+
t = Thread(target=stage1)
51+
t.start()
52+
t.join()
53+
54+
event, = events
55+
56+
exception, = event["exception"]["values"]
57+
58+
assert exception["type"] == "ZeroDivisionError"
59+
assert exception["mechanism"] == {"type": "threading", "handled": False}
60+
61+
if propagate_hub:
62+
assert event["tags"]["stage1"] is True
63+
else:
64+
assert "stage1" not in event.get("tags", {})

0 commit comments

Comments
 (0)
0