Skip to content

Commit c64a1a4

Browse files
authored
feat(client-reports): Record event_processor client reports (getsentry#1281)
1 parent a3170d9 commit c64a1a4

File tree

4 files changed

+91
-0
lines changed

4 files changed

+91
-0
lines changed

sentry_sdk/client.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,9 +145,18 @@ def _prepare_event(
145145
event["timestamp"] = datetime.utcnow()
146146

147147
if scope is not None:
148+
is_transaction = event.get("type") == "transaction"
148149
event_ = scope.apply_to_event(event, hint)
150+
151+
# one of the event/error processors returned None
149152
if event_ is None:
153+
if self.transport:
154+
self.transport.record_lost_event(
155+
"event_processor",
156+
data_category=("transaction" if is_transaction else "error"),
157+
)
150158
return None
159+
151160
event = event_
152161

153162
if (

tests/conftest.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,25 @@ def append_envelope(envelope):
243243
return inner
244244

245245

246+
@pytest.fixture
247+
def capture_client_reports(monkeypatch):
248+
def inner():
249+
reports = []
250+
test_client = sentry_sdk.Hub.current.client
251+
252+
def record_lost_event(reason, data_category=None, item=None):
253+
if data_category is None:
254+
data_category = item.data_category
255+
return reports.append((reason, data_category))
256+
257+
monkeypatch.setattr(
258+
test_client.transport, "record_lost_event", record_lost_event
259+
)
260+
return reports
261+
262+
return inner
263+
264+
246265
@pytest.fixture
247266
def capture_events_forksafe(monkeypatch, capture_events, request):
248267
def inner():

tests/integrations/gcp/test_gcp.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ def init_sdk(timeout_warning=False, **extra_init_args):
8181
transport=TestTransport,
8282
integrations=[GcpIntegration(timeout_warning=timeout_warning)],
8383
shutdown_timeout=10,
84+
# excepthook -> dedupe -> event_processor client report gets added
85+
# which we don't really care about for these tests
86+
send_client_reports=False,
8487
**extra_init_args
8588
)
8689

tests/test_basics.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
import sys
23
import logging
34

45
import pytest
@@ -10,13 +11,19 @@
1011
capture_event,
1112
capture_exception,
1213
capture_message,
14+
start_transaction,
1315
add_breadcrumb,
1416
last_event_id,
1517
Hub,
1618
)
1719

20+
from sentry_sdk._compat import reraise
1821
from sentry_sdk.integrations import _AUTO_ENABLING_INTEGRATIONS
1922
from sentry_sdk.integrations.logging import LoggingIntegration
23+
from sentry_sdk.scope import ( # noqa: F401
24+
add_global_event_processor,
25+
global_event_processors,
26+
)
2027

2128

2229
def test_processors(sentry_init, capture_events):
@@ -371,3 +378,56 @@ def test_capture_event_with_scope_kwargs(sentry_init, capture_events):
371378
(event,) = events
372379
assert event["level"] == "info"
373380
assert event["extra"]["foo"] == "bar"
381+
382+
383+
def test_dedupe_event_processor_drop_records_client_report(
384+
sentry_init, capture_events, capture_client_reports
385+
):
386+
"""
387+
DedupeIntegration internally has an event_processor that filters duplicate exceptions.
388+
We want a duplicate exception to be captured only once and the drop being recorded as
389+
a client report.
390+
"""
391+
sentry_init()
392+
events = capture_events()
393+
reports = capture_client_reports()
394+
395+
try:
396+
raise ValueError("aha!")
397+
except Exception:
398+
try:
399+
capture_exception()
400+
reraise(*sys.exc_info())
401+
except Exception:
402+
capture_exception()
403+
404+
(event,) = events
405+
(report,) = reports
406+
407+
assert event["level"] == "error"
408+
assert "exception" in event
409+
assert report == ("event_processor", "error")
410+
411+
412+
def test_event_processor_drop_records_client_report(
413+
sentry_init, capture_events, capture_client_reports
414+
):
415+
sentry_init(traces_sample_rate=1.0)
416+
events = capture_events()
417+
reports = capture_client_reports()
418+
419+
global global_event_processors
420+
421+
@add_global_event_processor
422+
def foo(event, hint):
423+
return None
424+
425+
capture_message("dropped")
426+
427+
with start_transaction(name="dropped"):
428+
pass
429+
430+
assert len(events) == 0
431+
assert reports == [("event_processor", "error"), ("event_processor", "transaction")]
432+
433+
global_event_processors.pop()

0 commit comments

Comments
 (0)