Skip to content

Commit 5ae47c8

Browse files
committed
Merge branch 'master' into major/3.0
2 parents b5b96b8 + b499d89 commit 5ae47c8

9 files changed

Lines changed: 191 additions & 12 deletions

File tree

‎sentry_sdk/client.py‎

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -598,14 +598,22 @@ def _prepare_event(
598598
before_send = self.options["before_send"]
599599
if before_send is not None and event is not None:
600600
new_event = None
601+
exception_raised_in_before_send = False
601602
with capture_internal_exceptions():
602-
new_event = before_send(event, hint or {})
603+
try:
604+
new_event = before_send(event, hint or {})
605+
except Exception:
606+
exception_raised_in_before_send = True
607+
raise
603608
if new_event is None:
604609
logger.info("before send dropped event")
605610
if self.transport:
606-
self.transport.record_lost_event(
607-
"before_send", data_category="error"
611+
reason = (
612+
"callback_error"
613+
if exception_raised_in_before_send
614+
else "before_send"
608615
)
616+
self.transport.record_lost_event(reason, data_category="error")
609617

610618
# If this is an exception, reset the DedupeIntegration. It still
611619
# remembers the dropped exception as the last exception, meaning
@@ -853,15 +861,32 @@ def _capture_telemetry(
853861
exception_raised_in_before_send_func = True
854862
raise
855863

856-
if ty in ("log", "metric"):
857-
# We are ok with dropping metrics and logs when an exception is raised
858-
# because we allow users to drop them in their respect before_send_*
859-
# functions.
864+
if ty == "log":
865+
if exception_raised_in_before_send_func:
866+
if self.transport:
867+
self.transport.record_lost_event(
868+
"callback_error", data_category="log_item"
869+
)
870+
return
871+
if serialized is None:
872+
if self.transport:
873+
self.transport.record_lost_event(
874+
"before_send", data_category="log_item"
875+
)
876+
return
877+
878+
elif ty == "metric":
860879
if exception_raised_in_before_send_func:
880+
if self.transport:
881+
self.transport.record_lost_event(
882+
"callback_error", data_category="trace_metric"
883+
)
861884
return
862-
# Logs and metrics can be dropped in their respective
863-
# before_send, so if we get None, don't queue them for sending.
864885
if serialized is None:
886+
if self.transport:
887+
self.transport.record_lost_event(
888+
"before_send", data_category="trace_metric"
889+
)
865890
return
866891

867892
elif ty == "span" and isinstance(telemetry, Span):

‎sentry_sdk/integrations/aws_lambda.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ def event_processor(
399399
if filtered_qs:
400400
request["query_string"] = filtered_qs
401401

402-
if "headers" in aws_event:
402+
if "headers" in aws_event and isinstance(aws_event["headers"], dict):
403403
request["headers"] = _filter_headers(aws_event["headers"])
404404

405405
if data_collection["user_info"]:
@@ -428,7 +428,7 @@ def _get_url(aws_event: "Any", aws_context: "Any") -> str:
428428
path = aws_event.get("path", None)
429429

430430
headers = aws_event.get("headers")
431-
if headers is None:
431+
if not isinstance(headers, dict):
432432
headers = {}
433433

434434
host = headers.get("Host", None)

‎sentry_sdk/scope.py‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1480,7 +1480,9 @@ def run_error_processors(self, event: "Event", hint: "Hint") -> "Optional[Event]
14801480
)
14811481

14821482
for error_processor in error_processors:
1483-
new_event = error_processor(event, exc_info)
1483+
new_event = event
1484+
with capture_internal_exceptions():
1485+
new_event = error_processor(event, exc_info)
14841486
if new_event is None:
14851487
return self._drop(error_processor, "error processor")
14861488

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Need to add some ignore rules in this directory, because the unit tests will add the Sentry SDK and its dependencies
2+
# into this directory to create a Lambda function package that contains everything needed to instrument a Lambda function using Sentry.
3+
4+
# Ignore everything
5+
*
6+
7+
# But not index.py
8+
!index.py
9+
10+
# And not .gitignore itself
11+
!.gitignore
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Need to add some ignore rules in this directory, because the unit tests will add the Sentry SDK and its dependencies
2+
# into this directory to create a Lambda function package that contains everything needed to instrument a Lambda function using Sentry.
3+
4+
# Ignore everything
5+
*
6+
7+
# But not index.py
8+
!index.py
9+
10+
# And not .gitignore itself
11+
!.gitignore

‎tests/test_basics.py‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,39 @@ def foo(event, hint):
661661
sentry_sdk.scope.global_event_processors = old_processors
662662

663663

664+
@pytest.mark.tests_internal_exceptions
665+
def test_before_send_exception_records_callback_error(
666+
sentry_init, capture_events, capture_record_lost_event_calls
667+
):
668+
def bad_before_send(event, hint):
669+
raise ValueError("before_send error")
670+
671+
sentry_init(before_send=bad_before_send, default_integrations=False)
672+
events = capture_events()
673+
record_lost_event_calls = capture_record_lost_event_calls()
674+
675+
capture_message("should be dropped")
676+
677+
assert len(events) == 0
678+
assert ("callback_error", "error", None, 1) in record_lost_event_calls
679+
680+
681+
def test_before_send_returning_none_records_before_send(
682+
sentry_init, capture_events, capture_record_lost_event_calls
683+
):
684+
def dropping_before_send(event, hint):
685+
return None
686+
687+
sentry_init(before_send=dropping_before_send)
688+
events = capture_events()
689+
record_lost_event_calls = capture_record_lost_event_calls()
690+
691+
capture_message("should be dropped")
692+
693+
assert len(events) == 0
694+
assert ("before_send", "error", None, 1) in record_lost_event_calls
695+
696+
664697
@pytest.mark.parametrize(
665698
"installed_integrations, expected_name",
666699
[

‎tests/test_logs.py‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,43 @@ def _before_log(record, hint):
127127
assert not logs
128128

129129

130+
@pytest.mark.tests_internal_exceptions
131+
def test_logs_before_send_log_raises_records_callback_error(
132+
sentry_init, capture_items, capture_record_lost_event_calls
133+
):
134+
def _before_log(record, hint):
135+
raise ValueError("before_send_log error")
136+
137+
sentry_init(before_send_log=_before_log)
138+
items = capture_items("log")
139+
record_lost_event_calls = capture_record_lost_event_calls()
140+
141+
sentry_sdk.logger.error("This is an error log...")
142+
143+
get_client().flush()
144+
145+
assert not items
146+
assert ("callback_error", "log_item", None, 1) in record_lost_event_calls
147+
148+
149+
def test_logs_before_send_log_returns_none_records_before_send(
150+
sentry_init, capture_items, capture_record_lost_event_calls
151+
):
152+
def _before_log(record, hint):
153+
return None
154+
155+
sentry_init(before_send_log=_before_log)
156+
items = capture_items("log")
157+
record_lost_event_calls = capture_record_lost_event_calls()
158+
159+
sentry_sdk.logger.error("This is an error log...")
160+
161+
get_client().flush()
162+
163+
assert not items
164+
assert ("before_send", "log_item", None, 1) in record_lost_event_calls
165+
166+
130167
def test_logs_attributes(sentry_init, capture_items):
131168
"""
132169
Passing arbitrary attributes to log messages.

‎tests/test_metrics.py‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,43 @@ def _before_metric(record, hint):
200200
assert not metrics
201201

202202

203+
@pytest.mark.tests_internal_exceptions
204+
def test_metrics_before_send_raises_records_callback_error(
205+
sentry_init, capture_items, capture_record_lost_event_calls
206+
):
207+
def _before_metric(record, hint):
208+
raise ValueError("before_send_metric error")
209+
210+
sentry_init(before_send_metric=_before_metric)
211+
items = capture_items("trace_metric")
212+
record_lost_event_calls = capture_record_lost_event_calls()
213+
214+
sentry_sdk.metrics.count("test.keep", 1)
215+
216+
get_client().flush()
217+
218+
assert not items
219+
assert ("callback_error", "trace_metric", None, 1) in record_lost_event_calls
220+
221+
222+
def test_metrics_before_send_returns_none_records_before_send(
223+
sentry_init, capture_items, capture_record_lost_event_calls
224+
):
225+
def _before_metric(record, hint):
226+
return None
227+
228+
sentry_init(before_send_metric=_before_metric)
229+
items = capture_items("trace_metric")
230+
record_lost_event_calls = capture_record_lost_event_calls()
231+
232+
sentry_sdk.metrics.count("test.drop", 1)
233+
234+
get_client().flush()
235+
236+
assert not items
237+
assert ("before_send", "trace_metric", None, 1) in record_lost_event_calls
238+
239+
203240
def test_transport_format(sentry_init, capture_envelopes):
204241
sentry_init(server_name="test-server", release="1.0.0")
205242

‎tests/tracing/test_span_streaming.py‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,29 @@ def traces_sampler(sampling_context):
466466
assert span["attributes"]["drop"] is False
467467

468468

469+
@pytest.mark.tests_internal_exceptions
470+
def test_traces_sampler_exception_falls_back(sentry_init, capture_items):
471+
def traces_sampler(sampling_context):
472+
raise ValueError("traces_sampler error")
473+
474+
sentry_init(
475+
traces_sampler=traces_sampler,
476+
traces_sample_rate=1.0,
477+
trace_lifecycle="stream",
478+
)
479+
480+
items = capture_items("span")
481+
482+
with sentry_sdk.traces.start_span(name="test"):
483+
...
484+
485+
sentry_sdk.get_client().flush()
486+
spans = [item.payload for item in items]
487+
488+
# Falls back to traces_sample_rate=1.0, so span should still be sent
489+
assert len(spans) == 1
490+
491+
469492
def test_traces_sampler_called_once_per_segment(sentry_init):
470493
traces_sampler_called = 0
471494
span_name_in_traces_sampler = None

0 commit comments

Comments
 (0)