Skip to content

Commit 16a9e6a

Browse files
authored
feat: lazy request processing (#12)
* feat: lazy request processing * chore: stylefixes
1 parent 02e5442 commit 16a9e6a

File tree

6 files changed

+63
-52
lines changed

6 files changed

+63
-52
lines changed

sentry_sdk/hub.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ def push_scope(self):
174174
def pop_scope_unsafe(self):
175175
"""Pops a scope layer from the stack. Try to use the context manager
176176
`push_scope()` instead."""
177+
self._pending_processors = []
177178
self._stack.pop()
178179

179180
@contextmanager

sentry_sdk/integrations/_wsgi.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,12 @@ class RequestExtractor(object):
3737
def __init__(self, request):
3838
self.request = request
3939

40-
def extract_into_scope(self, scope):
40+
def extract_into_event(self, event):
41+
if "request" in event:
42+
return
43+
4144
# if the code below fails halfway through we at least have some data
42-
scope.request = request_info = {}
45+
event["request"] = request_info = {}
4346

4447
request_info["url"] = self.url
4548
request_info["query_string"] = self.query_string

sentry_sdk/integrations/django/__init__.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,26 @@ def process_request(self, request):
3434
try:
3535
get_current_hub().push_scope()
3636

37+
get_current_hub().add_event_processor(
38+
lambda: self.make_event_processor(request)
39+
)
40+
3741
with configure_scope() as scope:
3842
scope.transaction = _get_transaction_from_request(request)
39-
try:
40-
DjangoRequestExtractor(request).extract_into_scope(scope)
41-
except Exception:
42-
get_current_hub().capture_internal_exception()
43-
44-
# TODO: user info
45-
4643
except Exception:
4744
get_current_hub().capture_internal_exception()
4845

46+
def make_event_processor(self, request):
47+
def processor(event):
48+
try:
49+
DjangoRequestExtractor(request).extract_into_event(event)
50+
except Exception:
51+
get_current_hub().capture_internal_exception()
52+
53+
# TODO: user info
54+
55+
return processor
56+
4957

5058
class DjangoRequestExtractor(RequestExtractor):
5159
@property

sentry_sdk/integrations/flask.py

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -25,46 +25,48 @@ def init_app(self, app):
2525
raise RuntimeError("Sentry registration is already registered")
2626
app.extensions[__name__] = True
2727

28-
appcontext_pushed.connect(_push_appctx, sender=app)
29-
app.teardown_appcontext(_pop_appctx)
30-
got_request_exception.connect(_capture_exception, sender=app)
31-
app.before_request(_before_request)
28+
appcontext_pushed.connect(self._push_appctx, sender=app)
29+
app.teardown_appcontext(self._pop_appctx)
30+
got_request_exception.connect(self._capture_exception, sender=app)
31+
app.before_request(self._before_request)
3232

33+
def _push_appctx(self, *args, **kwargs):
34+
get_current_hub().push_scope()
35+
_app_ctx_stack.top._sentry_app_scope_pushed = True
3336

34-
def _push_appctx(*args, **kwargs):
35-
get_current_hub().push_scope()
36-
_app_ctx_stack.top._sentry_app_scope_pushed = True
37+
def _pop_appctx(self, exception):
38+
get_current_hub().pop_scope_unsafe()
3739

40+
def _capture_exception(self, sender, exception, **kwargs):
41+
capture_exception(exception)
3842

39-
def _pop_appctx(exception):
40-
get_current_hub().pop_scope_unsafe()
43+
def _before_request(self, *args, **kwargs):
44+
try:
45+
assert getattr(
46+
_app_ctx_stack.top, "_sentry_app_scope_pushed", None
47+
), "scope push failed"
4148

49+
with configure_scope() as scope:
50+
if request.url_rule:
51+
scope.transaction = request.url_rule.endpoint
4252

43-
def _capture_exception(sender, exception, **kwargs):
44-
capture_exception(exception)
45-
46-
47-
def _before_request(*args, **kwargs):
48-
try:
49-
assert getattr(
50-
_app_ctx_stack.top, "_sentry_app_scope_pushed", None
51-
), "scope push failed"
52-
53-
with configure_scope() as scope:
54-
if request.url_rule:
55-
scope.transaction = request.url_rule.endpoint
53+
get_current_hub().add_event_processor(self.make_event_processor)
54+
except Exception:
55+
get_current_hub().capture_internal_exception()
5656

57+
def make_event_processor(self):
58+
def processor(event):
5759
try:
58-
FlaskRequestExtractor(request).extract_into_scope(scope)
60+
FlaskRequestExtractor(request).extract_into_event(event)
5961
except Exception:
6062
get_current_hub().capture_internal_exception()
6163

6264
try:
63-
_set_user_info(scope)
65+
_set_user_info(event)
6466
except Exception:
6567
get_current_hub().capture_internal_exception()
66-
except Exception:
67-
get_current_hub().capture_internal_exception()
68+
69+
return processor
6870

6971

7072
class FlaskRequestExtractor(RequestExtractor):
@@ -96,7 +98,10 @@ def size_of_file(self, file):
9698
return file.content_length
9799

98100

99-
def _set_user_info(scope):
101+
def _set_user_info(event):
102+
if "user" in event:
103+
return
104+
100105
try:
101106
ip_address = request.access_route[0]
102107
except IndexError:
@@ -114,4 +119,4 @@ def _set_user_info(scope):
114119
# - no user is logged in
115120
pass
116121

117-
scope.user = user_info
122+
event["user"] = user_info

tests/integrations/flask/test_flask.py

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
from flask_login import LoginManager, login_user
1111

12-
from sentry_sdk import configure_scope, capture_message
12+
from sentry_sdk import capture_message
1313
import sentry_sdk.integrations.flask as flask_sentry
1414

1515
sentry = flask_sentry.FlaskSentry()
@@ -34,20 +34,16 @@ def hi():
3434
return app
3535

3636

37-
def test_has_context(app):
38-
@app.route("/")
39-
def index():
40-
with configure_scope() as scope:
41-
assert scope._data["transaction"] == "index"
42-
assert "data" not in scope._data["request"]
43-
assert scope._data["request"]["url"] == "http://localhost/"
44-
45-
return "ok"
46-
37+
def test_has_context(app, capture_events):
4738
client = app.test_client()
48-
response = client.get("/")
39+
response = client.get("/message")
4940
assert response.status_code == 200
5041

42+
event, = capture_events
43+
assert event["transaction"] == "hi"
44+
assert "data" not in event["request"]
45+
assert event["request"]["url"] == "http://localhost/message"
46+
5147

5248
@pytest.mark.parametrize("debug", (True, False))
5349
@pytest.mark.parametrize("testing", (True, False))

tests/test_utils.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from hypothesis import given, assume, settings
1+
from hypothesis import given, assume
22
import hypothesis.strategies as st
33

44
from sentry_sdk.utils import safe_repr
@@ -8,15 +8,13 @@
88

99

1010
@given(x=any_string)
11-
@settings(max_examples=1000)
1211
def test_safe_repr_never_broken_for_strings(x):
1312
r = safe_repr(x)
1413
assert isinstance(r, text_type)
1514
assert u"broken repr" not in r
1615

1716

1817
@given(x=any_string)
19-
@settings(max_examples=1000)
2018
def test_safe_repr_never_leaves_escapes_in(x):
2119
if isinstance(x, bytes):
2220
assume(b"\\u" not in x and b"\\x" not in x)

0 commit comments

Comments
 (0)