Skip to content

Commit 11366c0

Browse files
add try except and more tests
1 parent 28095bd commit 11366c0

File tree

4 files changed

+171
-83
lines changed

4 files changed

+171
-83
lines changed

sentry_sdk/integrations/fastapi.py

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from typing import TYPE_CHECKING
1212

1313
if TYPE_CHECKING:
14-
from typing import Any, Callable, Dict
14+
from typing import Any, Callable, Dict, Optional
1515
from sentry_sdk._types import Event
1616

1717
try:
@@ -112,33 +112,38 @@ async def _sentry_app(*args, **kwargs):
112112
sentry_scope = sentry_sdk.get_isolation_scope()
113113
sentry_scope._name = FastApiIntegration.identifier
114114

115+
def _make_request_event_processor(info):
116+
# type: (Optional[Dict[str, Any]]) -> Callable[[Event, Dict[str, Any]], Event]
117+
def event_processor(event, hint):
118+
# type: (Event, Dict[str, Any]) -> Event
119+
120+
# Extract information from request
121+
request_info = event.get("request", {})
122+
if info:
123+
if "cookies" in info and should_send_default_pii():
124+
request_info["cookies"] = info["cookies"]
125+
if "data" in info:
126+
request_info["data"] = info["data"]
127+
event["request"] = deepcopy(request_info)
128+
129+
return event
130+
131+
return event_processor
132+
115133
try:
116134
response = await old_app(*args, **kwargs)
117-
finally:
135+
except Exception as exception:
118136
extractor = StarletteRequestExtractor(request)
119137
info = await extractor.extract_request_info()
120138

121-
def _make_request_event_processor(req, integration):
122-
# type: (Any, Any) -> Callable[[Event, Dict[str, Any]], Event]
123-
def event_processor(event, hint):
124-
# type: (Event, Dict[str, Any]) -> Event
125-
126-
# Extract information from request
127-
request_info = event.get("request", {})
128-
if info:
129-
if "cookies" in info and should_send_default_pii():
130-
request_info["cookies"] = info["cookies"]
131-
if "data" in info:
132-
request_info["data"] = info["data"]
133-
event["request"] = deepcopy(request_info)
139+
sentry_scope.add_event_processor(_make_request_event_processor(info))
134140

135-
return event
141+
raise exception
136142

137-
return event_processor
143+
extractor = StarletteRequestExtractor(request)
144+
info = await extractor.extract_request_info()
138145

139-
sentry_scope.add_event_processor(
140-
_make_request_event_processor(request, integration)
141-
)
146+
sentry_scope.add_event_processor(_make_request_event_processor(info))
142147

143148
return response
144149

sentry_sdk/integrations/starlette.py

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -501,20 +501,15 @@ async def _sentry_async_func(*args, **kwargs):
501501
sentry_scope = sentry_sdk.get_isolation_scope()
502502
sentry_scope._name = StarletteIntegration.identifier
503503

504-
response = await old_func(*args, **kwargs)
505-
506-
extractor = StarletteRequestExtractor(request)
507-
info = await extractor.extract_request_info()
508-
509-
def _make_request_event_processor(req, integration):
510-
# type: (Any, Any) -> Callable[[Event, dict[str, Any]], Event]
504+
def _make_request_event_processor(info):
505+
# type: (Optional[Dict[str, Any]]) -> Callable[[Event, Dict[str, Any]], Event]
511506
def event_processor(event, hint):
512507
# type: (Event, Dict[str, Any]) -> Event
513508

514-
# Add info from request to event
509+
# Extract information from request
515510
request_info = event.get("request", {})
516511
if info:
517-
if "cookies" in info:
512+
if "cookies" in info and should_send_default_pii():
518513
request_info["cookies"] = info["cookies"]
519514
if "data" in info:
520515
request_info["data"] = info["data"]
@@ -524,9 +519,22 @@ def event_processor(event, hint):
524519

525520
return event_processor
526521

527-
sentry_scope.add_event_processor(
528-
_make_request_event_processor(request, integration)
529-
)
522+
try:
523+
response = await old_func(*args, **kwargs)
524+
except Exception as exception:
525+
extractor = StarletteRequestExtractor(request)
526+
info = await extractor.extract_request_info()
527+
528+
sentry_scope.add_event_processor(
529+
_make_request_event_processor(info)
530+
)
531+
532+
raise exception
533+
534+
extractor = StarletteRequestExtractor(request)
535+
info = await extractor.extract_request_info()
536+
537+
sentry_scope.add_event_processor(_make_request_event_processor(info))
530538

531539
return response
532540

tests/integrations/fastapi/test_fastapi.py

Lines changed: 62 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from fastapi.middleware.trustedhost import TrustedHostMiddleware
1313

1414
import sentry_sdk
15-
from sentry_sdk import capture_message
15+
from sentry_sdk import capture_message, capture_exception
1616
from sentry_sdk.feature_flags import add_feature_flag
1717
from sentry_sdk.integrations.asgi import SentryAsgiMiddleware
1818
from sentry_sdk.integrations.fastapi import FastApiIntegration
@@ -75,29 +75,6 @@ async def _thread_ids_async():
7575
return app
7676

7777

78-
def test_stream_available_in_handler(sentry_init):
79-
sentry_init(
80-
integrations=[StarletteIntegration(), FastApiIntegration()],
81-
)
82-
83-
app = FastAPI()
84-
85-
@app.post("/consume")
86-
async def _consume_stream_body(request):
87-
# Avoid cache by constructing new request
88-
wrapped_request = Request(request.scope, request.receive)
89-
90-
assert await asyncio.wait_for(wrapped_request.json(), timeout=1.0) == BODY_JSON
91-
92-
return {"status": "ok"}
93-
94-
client = TestClient(app)
95-
client.post(
96-
"/consume",
97-
json=BODY_JSON,
98-
)
99-
100-
10178
@pytest.mark.asyncio
10279
async def test_response(sentry_init, capture_events):
10380
# FastAPI is heavily based on Starlette so we also need
@@ -246,6 +223,67 @@ def test_active_thread_id(sentry_init, capture_envelopes, teardown_profiling, en
246223
assert str(data["active"]) == trace_context["data"]["thread.id"]
247224

248225

226+
@pytest.mark.asyncio
227+
def test_request_body_not_cached_with_exception(sentry_init, capture_events):
228+
sentry_init(
229+
integrations=[StarletteIntegration(), FastApiIntegration()],
230+
)
231+
232+
app = FastAPI()
233+
234+
@app.post("/exception")
235+
async def _exception(request: Request):
236+
1 / 0
237+
return {"error": "Oh no!"}
238+
239+
events = capture_events()
240+
241+
client = TestClient(app)
242+
243+
try:
244+
client.post(
245+
"/exception",
246+
json=BODY_JSON,
247+
)
248+
except Exception:
249+
capture_exception()
250+
251+
event = events[0]
252+
assert event["request"]["data"] == ""
253+
254+
255+
def test_request_body_cached_with_exception(sentry_init, capture_events):
256+
sentry_init(
257+
integrations=[StarletteIntegration(), FastApiIntegration()],
258+
)
259+
260+
app = FastAPI(debug=True)
261+
app.user_middleware = []
262+
app.middleware_stack = app.build_middleware_stack()
263+
264+
@app.post("/exception")
265+
async def _exception(request: Request):
266+
await request.json()
267+
1 / 0
268+
return {"error": "Oh no!"}
269+
270+
events = capture_events()
271+
272+
client = TestClient(app)
273+
274+
try:
275+
client.post(
276+
"/exception",
277+
json=BODY_JSON,
278+
)
279+
except Exception:
280+
print("capturing")
281+
capture_exception()
282+
283+
event = events[0]
284+
assert event["request"]["data"] == BODY_JSON
285+
286+
249287
@pytest.mark.asyncio
250288
async def test_original_request_not_scrubbed(sentry_init, capture_events):
251289
sentry_init(

tests/integrations/starlette/test_starlette.py

Lines changed: 64 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
import pytest
1313

14-
from sentry_sdk import capture_message, get_baggage, get_traceparent
14+
from sentry_sdk import capture_message, get_baggage, get_traceparent, capture_exception
1515
from sentry_sdk.integrations.asgi import SentryAsgiMiddleware
1616
from sentry_sdk.integrations.starlette import (
1717
StarletteIntegration,
@@ -509,32 +509,6 @@ async def test_starletterequestextractor_extract_request_info_no_pii(sentry_init
509509
assert request_info["data"] == BODY_JSON
510510

511511

512-
def test_stream_available_in_handler(sentry_init):
513-
sentry_init(
514-
integrations=[StarletteIntegration()],
515-
)
516-
517-
async def _consume_stream_body(request):
518-
# Avoid cache by constructing new request
519-
wrapped_request = Request(request.scope, request.receive)
520-
521-
assert await asyncio.wait_for(wrapped_request.json(), timeout=1.0) == BODY_JSON
522-
523-
return starlette.responses.JSONResponse({"status": "ok"})
524-
525-
app = starlette.applications.Starlette(
526-
routes=[
527-
starlette.routing.Route("/consume", _consume_stream_body, methods=["POST"]),
528-
],
529-
)
530-
531-
client = TestClient(app)
532-
client.post(
533-
"/consume",
534-
json=BODY_JSON,
535-
)
536-
537-
538512
@pytest.mark.parametrize(
539513
"url,transaction_style,expected_transaction,expected_source",
540514
[
@@ -1001,6 +975,69 @@ def test_active_thread_id(sentry_init, capture_envelopes, teardown_profiling, en
1001975
assert str(data["active"]) == trace_context["data"]["thread.id"]
1002976

1003977

978+
def test_request_body_not_cached_with_exception(sentry_init, capture_events):
979+
sentry_init(
980+
integrations=[StarletteIntegration()],
981+
)
982+
983+
events = capture_events()
984+
985+
async def _exception(request):
986+
1 / 0
987+
return {"error": "Oh no!"}
988+
989+
app = starlette.applications.Starlette(
990+
routes=[
991+
starlette.routing.Route("/exception", _exception, methods=["POST"]),
992+
],
993+
)
994+
995+
client = TestClient(app)
996+
997+
try:
998+
client.post(
999+
"/exception",
1000+
json=BODY_JSON,
1001+
)
1002+
except ZeroDivisionError:
1003+
capture_exception()
1004+
1005+
event = events[0]
1006+
assert event["request"]["data"] == ""
1007+
1008+
1009+
def test_request_body_cached_with_exception(sentry_init, capture_events):
1010+
sentry_init(
1011+
integrations=[StarletteIntegration()],
1012+
)
1013+
1014+
events = capture_events()
1015+
1016+
async def _exception(request):
1017+
request.json()
1018+
1 / 0
1019+
return {"error": "Oh no!"}
1020+
1021+
app = starlette.applications.Starlette(
1022+
routes=[
1023+
starlette.routing.Route("/exception", _exception, methods=["POST"]),
1024+
],
1025+
)
1026+
1027+
client = TestClient(app)
1028+
1029+
try:
1030+
client.post(
1031+
"/exception",
1032+
json=BODY_JSON,
1033+
)
1034+
except ZeroDivisionError:
1035+
capture_exception()
1036+
1037+
event = events[0]
1038+
assert event["request"]["data"] == BODY_JSON
1039+
1040+
10041041
def test_original_request_not_scrubbed(sentry_init, capture_events):
10051042
sentry_init(
10061043
default_integrations=False,

0 commit comments

Comments
 (0)