Skip to content

Commit fc6fc30

Browse files
authored
ref: Use atexit only in init and implement it as integration (#45)
1 parent dde5857 commit fc6fc30

File tree

13 files changed

+271
-144
lines changed

13 files changed

+271
-144
lines changed

sentry_sdk/_compat.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,27 @@ def __new__(cls, name, this_bases, d):
5353
return meta(name, bases, d)
5454

5555
return type.__new__(metaclass, "temporary_class", (), {})
56+
57+
58+
def check_thread_support():
59+
try:
60+
from uwsgi import opt
61+
except ImportError:
62+
return
63+
64+
# When `threads` is passed in as a uwsgi option,
65+
# `enable-threads` is implied on.
66+
if "threads" in opt:
67+
return
68+
69+
if str(opt.get("enable-threads", "0")).lower() in ("false", "off", "no", "0"):
70+
from warnings import warn
71+
72+
warn(
73+
Warning(
74+
"We detected the use of uwsgi with disabled threads. "
75+
"This will cause issues with the transport you are "
76+
"trying to use. Please enable threading for uwsgi. "
77+
'(Enable the "enable-threads" flag).'
78+
)
79+
)

sentry_sdk/client.py

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import os
22
import uuid
33
import random
4-
import atexit
54
from datetime import datetime
65

76
from ._compat import string_types
@@ -11,6 +10,7 @@
1110
convert_types,
1211
handle_in_app,
1312
get_type_name,
13+
logger,
1414
)
1515
from .transport import make_transport
1616
from .consts import DEFAULT_OPTIONS, SDK_INFO
@@ -42,7 +42,7 @@ def get_options(*args, **kwargs):
4242
class Client(object):
4343
def __init__(self, *args, **kwargs):
4444
self.options = options = get_options(*args, **kwargs)
45-
self._transport = make_transport(options)
45+
self.transport = make_transport(options)
4646

4747
request_bodies = ("always", "never", "small", "medium")
4848
if options["request_bodies"] not in request_bodies:
@@ -52,9 +52,6 @@ def __init__(self, *args, **kwargs):
5252
)
5353
)
5454

55-
# XXX: we should probably only do this for the init()ed client
56-
atexit.register(self.close)
57-
5855
@property
5956
def dsn(self):
6057
"""Returns the configured dsn."""
@@ -84,7 +81,10 @@ def _prepare_event(self, event, hint, scope):
8481

8582
before_send = self.options["before_send"]
8683
if before_send is not None:
87-
event = before_send(event)
84+
new_event = before_send(event)
85+
if new_event is None:
86+
logger.info("before send dropped event (%s)", event)
87+
event = new_event
8888

8989
# Postprocess the event in the very end so that annotated types do
9090
# generally not surface in before_send
@@ -129,24 +129,28 @@ def _should_capture(self, event, hint=None, scope=None):
129129

130130
def capture_event(self, event, hint=None, scope=None):
131131
"""Captures an event."""
132-
if self._transport is None:
132+
if self.transport is None:
133133
return
134134
rv = event.get("event_id")
135135
if rv is None:
136136
event["event_id"] = rv = uuid.uuid4().hex
137137
if self._should_capture(event, hint, scope):
138138
event = self._prepare_event(event, hint, scope)
139139
if event is not None:
140-
self._transport.capture_event(event)
140+
self.transport.capture_event(event)
141141
return rv
142142

143-
def drain_events(self, timeout=None):
144-
if timeout is None:
145-
timeout = self.options["shutdown_timeout"]
146-
if self._transport is not None:
147-
self._transport.drain_events(timeout)
143+
def close(self, timeout=None, shutdown_callback=None):
144+
"""Closes the client which shuts down the transport in an
145+
orderly manner.
146+
"""
147+
if self.transport is not None:
148+
if timeout is None:
149+
timeout = self.options["shutdown_timeout"]
150+
self.transport.shutdown(timeout=timeout, callback=shutdown_callback)
151+
152+
def __enter__(self):
153+
return self
148154

149-
def close(self):
150-
self.drain_events()
151-
if self._transport is not None:
152-
self._transport.close()
155+
def __exit__(self, exc_type, exc_value, tb):
156+
self.close()

sentry_sdk/consts.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import socket
22

3+
34
VERSION = "0.1"
45
DEFAULT_SERVER_NAME = socket.gethostname() if hasattr(socket, "gethostname") else None
56
DEFAULT_OPTIONS = {

sentry_sdk/hub.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ def add_breadcrumb(self, *args, **kwargs):
158158
"""Adds a breadcrumb."""
159159
client, scope = self._stack[-1]
160160
if client is None:
161+
logger.info("Dropped breadcrumb because no client bound")
161162
return
162163

163164
if not kwargs and len(args) == 1 and callable(args[0]):
@@ -172,11 +173,14 @@ def add_breadcrumb(self, *args, **kwargs):
172173
if crumb.get("type") is None:
173174
crumb["type"] = "default"
174175

176+
original_crumb = crumb
175177
if client.options["before_breadcrumb"] is not None:
176178
crumb = client.options["before_breadcrumb"](crumb)
177179

178180
if crumb is not None:
179181
scope._breadcrumbs.append(crumb)
182+
else:
183+
logger.info("before breadcrumb dropped breadcrumb (%s)", original_crumb)
180184
while len(scope._breadcrumbs) >= client.options["max_breadcrumbs"]:
181185
scope._breadcrumbs.popleft()
182186

sentry_sdk/integrations/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@ def _get_default_integrations():
1111
from .logging import LoggingIntegration
1212
from .excepthook import ExcepthookIntegration
1313
from .dedupe import DedupeIntegration
14+
from .atexit import AtexitIntegration
1415

1516
yield LoggingIntegration
1617
yield ExcepthookIntegration
1718
yield DedupeIntegration
19+
yield AtexitIntegration
1820

1921

2022
def setup_integrations(options):

sentry_sdk/integrations/atexit.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from __future__ import absolute_import
2+
3+
import os
4+
import atexit
5+
6+
from sentry_sdk.hub import Hub
7+
from sentry_sdk.utils import logger
8+
from . import Integration
9+
10+
11+
def default_shutdown_callback(pending, timeout):
12+
print("Sentry is attempting to send %i pending error messages" % pending)
13+
print("Waiting up to %s seconds" % timeout)
14+
print("Press Ctrl-%s to quit" % (os.name == "nt" and "Break" or "C"))
15+
16+
17+
class AtexitIntegration(Integration):
18+
identifier = "atexit"
19+
20+
def __init__(self, callback=None):
21+
if callback is None:
22+
callback = default_shutdown_callback
23+
self.callback = callback
24+
25+
def install(self):
26+
@atexit.register
27+
def _shutdown():
28+
main_client = Hub.main.client
29+
logger.debug("atexit: got shutdown signal")
30+
if main_client is not None:
31+
logger.debug("atexit: shutting down client")
32+
main_client.close(shutdown_callback=self.callback)

sentry_sdk/scope.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
from .utils import logger
2+
3+
14
class Scope(object):
25
__slots__ = ["_data", "_breadcrumbs", "_event_processors", "_error_processors"]
36

@@ -73,6 +76,9 @@ def func(event, exc_info):
7376
self._error_processors.append(func)
7477

7578
def apply_to_event(self, event, hint=None):
79+
def _drop(event, cause, ty):
80+
logger.info("%s (%s) dropped event (%s)", ty, cause, event)
81+
7682
event.setdefault("breadcrumbs", []).extend(self._breadcrumbs)
7783
if event.get("user") is None and "user" in self._data:
7884
event["user"] = self._data["user"]
@@ -99,14 +105,16 @@ def apply_to_event(self, event, hint=None):
99105
if hint is not None and hint.exc_info is not None:
100106
exc_info = hint.exc_info
101107
for processor in self._error_processors:
102-
event = processor(event, exc_info)
103-
if event is None:
104-
return
108+
new_event = processor(event, exc_info)
109+
if new_event is None:
110+
return _drop(event, processor, "error processor")
111+
event = new_event
105112

106113
for processor in self._event_processors:
107-
event = processor(event)
108-
if event is None:
109-
return None
114+
new_event = processor(event)
115+
if new_event is None:
116+
return _drop(event, processor, "event processor")
117+
event = new_event
110118

111119
return event
112120

0 commit comments

Comments
 (0)