Skip to content

Commit 673ebcf

Browse files
committed
chore: switch back to integration objects
1 parent 3993cd3 commit 673ebcf

10 files changed

Lines changed: 120 additions & 161 deletions

File tree

sentry_sdk/client.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
from .transport import Transport
66
from .consts import DEFAULT_OPTIONS, SDK_INFO
77
from .stripping import strip_event, flatten_metadata
8-
from .integrations import get_integration
98

109

1110
NO_DSN = object()
@@ -31,15 +30,8 @@ def __init__(self, dsn=None, *args, **kwargs):
3130
self._transport = Transport(dsn)
3231
self._transport.start()
3332

34-
integrations = options.get("integrations", ["logging"]) or ()
35-
if not isinstance(integrations, dict):
36-
integrations = {name: {} for name in integrations}
37-
38-
for name, options in integrations.items():
39-
install_fn = get_integration(name)
40-
if options is None:
41-
options = {}
42-
install_fn(self, **options)
33+
for integration in options.get("integrations", ["logging"]) or ():
34+
integration(self)
4335

4436
@property
4537
def dsn(self):
Lines changed: 22 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,31 @@
1-
_locked = False
2-
_integrations = {}
1+
from __future__ import print_function
32

3+
import sys
4+
from threading import Lock
45

5-
def register_integration(identifier, integration=None):
6-
def inner(integration):
7-
if _locked:
8-
raise ValueError(
9-
"A client has already been initialized. "
10-
"Registration of integrations no longer possible"
11-
)
12-
if identifier in _integrations:
13-
raise ValueError("Integration with that name already exists.")
14-
_integrations[identifier] = integration
156

16-
if integration is not None:
17-
return inner(integration)
18-
return inner
7+
_installer_lock = Lock()
8+
_installed_integrations = {}
199

2010

21-
def get_integration(identifier):
22-
global _locked
23-
_locked = True
24-
return _integrations[identifier]
11+
class Integration(object):
12+
identifier = None
2513

14+
def __init__(self, **kwargs):
15+
"""Initialize an integration."""
16+
raise NotImplementedError()
2617

27-
@register_integration("django")
28-
def _django_integration(*a, **kw):
29-
from .django import install
18+
def install(self, client):
19+
raise NotImplementedError()
3020

31-
return install(*a, **kw)
21+
def __call__(self, client):
22+
assert self.identifier
23+
with _installer_lock:
24+
if self.identifier in _installed_integrations:
25+
print("warning: %s integration for Sentry is already "
26+
"configured. Will ignore second configuration." % self.identifier,
27+
file=sys.stderr)
28+
return
3229

33-
34-
@register_integration("flask")
35-
def _flask_integration(*a, **kw):
36-
from .flask import install
37-
38-
return install(*a, **kw)
39-
40-
41-
@register_integration("celery")
42-
def _celery_integration(*a, **kw):
43-
from .celery import install
44-
45-
return install(*a, **kw)
46-
47-
48-
@register_integration("logging")
49-
def _logging_integration(*a, **kw):
50-
from .logging import install
51-
52-
return install(*a, **kw)
30+
self.install(client)
31+
_installed_integrations[self.identifier] = self

sentry_sdk/integrations/celery.py

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,40 @@
11
from __future__ import absolute_import
22

3-
from threading import Lock
4-
53
from celery.signals import task_failure, task_prerun, task_postrun
64

75
from sentry_sdk import get_current_hub, configure_scope, capture_exception
86
from sentry_sdk.hub import _internal_exceptions
97

10-
11-
_installer_lock = Lock()
12-
_installed = False
8+
from . import Integration
139

1410

15-
def install(client):
16-
global _installed
17-
with _installer_lock:
18-
if _installed:
19-
return
11+
class CeleryIntegration(Integration):
12+
identifier = 'celery'
2013

21-
task_prerun.connect(_handle_task_prerun, weak=False)
22-
task_postrun.connect(_handle_task_postrun, weak=False)
23-
task_failure.connect(_process_failure_signal, weak=False)
14+
def __init__(self):
15+
pass
2416

25-
_installed = True
17+
def install(self, client):
18+
task_prerun.connect(self._handle_task_prerun, weak=False)
19+
task_postrun.connect(self._handle_task_postrun, weak=False)
20+
task_failure.connect(self._process_failure_signal, weak=False)
2621

2722

28-
def _process_failure_signal(sender, task_id, einfo, **kw):
29-
if hasattr(sender, "throws") and isinstance(einfo.exception, sender.throws):
30-
return
23+
def _process_failure_signal(self, sender, task_id, einfo, **kw):
24+
if hasattr(sender, "throws") and isinstance(einfo.exception,
25+
sender.throws):
26+
return
3127

32-
capture_exception(einfo.exc_info)
28+
capture_exception(einfo.exc_info)
3329

3430

35-
def _handle_task_prerun(sender, task, **kw):
36-
with _internal_exceptions():
37-
get_current_hub().push_scope()
31+
def _handle_task_prerun(self, sender, task, **kw):
32+
with _internal_exceptions():
33+
get_current_hub().push_scope()
3834

39-
with configure_scope() as scope:
40-
scope.transaction = task.name
35+
with configure_scope() as scope:
36+
scope.transaction = task.name
4137

4238

43-
def _handle_task_postrun(sender, task_id, task, **kw):
44-
get_current_hub().pop_scope_unsafe()
39+
def _handle_task_postrun(self, sender, task_id, task, **kw):
40+
get_current_hub().pop_scope_unsafe()

sentry_sdk/integrations/django.py

Lines changed: 44 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
from __future__ import absolute_import
22

3-
from threading import Lock
4-
53
from django.core import signals
64

75
try:
@@ -12,6 +10,50 @@
1210
from sentry_sdk import get_current_hub, configure_scope, capture_exception
1311
from sentry_sdk.hub import _internal_exceptions
1412
from ._wsgi import RequestExtractor
13+
from . import Integration
14+
15+
16+
class DjangoIntegration(Integration):
17+
identifier = 'django'
18+
19+
def __init__(self):
20+
pass
21+
22+
def install(self, client):
23+
from django.core.handlers.base import BaseHandler
24+
25+
make_event_processor = self._make_event_processor
26+
27+
old_get_response = BaseHandler.get_response
28+
29+
def sentry_patched_get_response(self, request):
30+
with get_current_hub().push_scope():
31+
get_current_hub().add_event_processor(
32+
lambda: make_event_processor(request)
33+
)
34+
35+
with configure_scope() as scope:
36+
scope.transaction = resolve(request.path).func.__name__
37+
38+
return old_get_response(self, request)
39+
40+
BaseHandler.get_response = sentry_patched_get_response
41+
42+
signals.got_request_exception.connect(_got_request_exception)
43+
44+
def _make_event_processor(self, request):
45+
def processor(event):
46+
with _internal_exceptions():
47+
DjangoRequestExtractor(request).extract_into_event(event)
48+
49+
# TODO: user info
50+
51+
return processor
52+
53+
54+
def _got_request_exception(request=None, **kwargs):
55+
capture_exception()
56+
1557

1658

1759
class DjangoRequestExtractor(RequestExtractor):
@@ -41,52 +83,3 @@ def files(self):
4183

4284
def size_of_file(self, file):
4385
return file.size
44-
45-
46-
_installer_lock = Lock()
47-
_installed = False
48-
49-
50-
def install(client):
51-
global _installed
52-
with _installer_lock:
53-
if _installed:
54-
return
55-
56-
_install_impl()
57-
_installed = True
58-
59-
60-
def _install_impl():
61-
from django.core.handlers.base import BaseHandler
62-
63-
old_get_response = BaseHandler.get_response
64-
65-
def sentry_patched_get_response(self, request):
66-
with get_current_hub().push_scope():
67-
get_current_hub().add_event_processor(
68-
lambda: _make_event_processor(request)
69-
)
70-
71-
with configure_scope() as scope:
72-
scope.transaction = resolve(request.path).func.__name__
73-
74-
return old_get_response(self, request)
75-
76-
BaseHandler.get_response = sentry_patched_get_response
77-
78-
signals.got_request_exception.connect(_got_request_exception)
79-
80-
81-
def _make_event_processor(request):
82-
def processor(event):
83-
with _internal_exceptions():
84-
DjangoRequestExtractor(request).extract_into_event(event)
85-
86-
# TODO: user info
87-
88-
return processor
89-
90-
91-
def _got_request_exception(request=None, **kwargs):
92-
capture_exception()

sentry_sdk/integrations/flask.py

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
from __future__ import absolute_import
22

3-
from threading import Lock
4-
53
from sentry_sdk import capture_exception, get_current_hub
64
from sentry_sdk.hub import _internal_exceptions
75
from ._wsgi import RequestExtractor
6+
from . import Integration
87

98
try:
109
from flask_login import current_user
@@ -19,22 +18,17 @@
1918
)
2019

2120

22-
_installer_lock = Lock()
23-
_installed = False
24-
21+
class FlaskIntegration(Integration):
22+
identifier = "flask"
2523

26-
def install(client):
27-
global _installed
28-
with _installer_lock:
29-
if _installed:
30-
return
24+
def __init__(self):
25+
pass
3126

27+
def install(self, client):
3228
appcontext_pushed.connect(_push_appctx)
3329
appcontext_tearing_down.connect(_pop_appctx)
3430
got_request_exception.connect(_capture_exception)
3531

36-
_installed = True
37-
3832

3933
def _push_appctx(*args, **kwargs):
4034
get_current_hub().push_scope()

sentry_sdk/integrations/logging.py

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,37 +3,30 @@
33

44
import sys
55
import logging
6-
from threading import Lock
76

87
from sentry_sdk import get_current_hub, capture_event, add_breadcrumb
98
from sentry_sdk.utils import to_string, Event, skip_internal_frames
109
from sentry_sdk.hub import _internal_exceptions
1110

11+
from . import Integration
1212

13-
_installer_lock = Lock()
14-
_installed = False
15-
_master_handler = None
1613

14+
class LoggingIntegration(Integration):
15+
identifier = 'logging'
16+
def __init__(self, level=logging.INFO, event_level=None):
17+
self._handler = SentryHandler(level=level, event_level=event_level)
1718

18-
def install(client, level=logging.INFO, event_level=None):
19-
global _installed
20-
global _master_handler
21-
with _installer_lock:
22-
if _installed:
23-
return
24-
25-
_master_handler = SentryHandler(level=level, event_level=event_level)
19+
def install(self, client):
20+
handler = self._handler
2621

2722
old_callhandlers = logging.Logger.callHandlers
2823

2924
def sentry_patched_callhandlers(self, record):
30-
_master_handler.handle(record)
25+
handler.handle(record)
3126
return old_callhandlers(self, record)
3227

3328
logging.Logger.callHandlers = sentry_patched_callhandlers
3429

35-
_installed = True
36-
3730

3831
class SentryHandler(logging.Handler, object):
3932
def __init__(self, level, event_level):

tests/integrations/celery/test_celery.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22

33
pytest.importorskip("celery")
44

5+
from sentry_sdk.integrations.celery import CeleryIntegration
6+
57
from celery import Celery
68

79

810
@pytest.fixture
911
def celery(sentry_init):
10-
sentry_init(integrations=["celery"])
12+
sentry_init(integrations=[CeleryIntegration()])
1113
celery = Celery(__name__)
1214
celery.conf.CELERY_ALWAYS_EAGER = True
1315
return celery

tests/integrations/django/myapp/settings.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
import os
1414
import sentry_sdk
1515

16+
from sentry_sdk.integrations.django import DjangoIntegration
17+
1618

1719
try:
1820
# Django >= 1.10
@@ -129,4 +131,6 @@ def process_response(self, request, response):
129131

130132
STATIC_URL = "/static/"
131133

132-
sentry_sdk.get_current_hub().bind_client(sentry_sdk.Client(integrations=["django"]))
134+
sentry_sdk.get_current_hub().bind_client(sentry_sdk.Client(integrations=[
135+
DjangoIntegration()
136+
]))

0 commit comments

Comments
 (0)