Skip to content

Commit 6a4eb20

Browse files
authored
fix(crons): Return None from capture_checkin when nothing is sent (#7868)
### Description `capture_checkin` returned a check-in id even when the check-in wasn't sent, because it ignored the return value of `capture_event`. That happens when there is no active client, or when `before_send` drops the event. A caller that hands the id to another process (as in the issue: the scheduler opens the check-in, the worker closes it, and opens its own only when no id arrives) can't tell that the check-in never reached Sentry. It now returns `None` in that case, the same way `capture_event` does. `monitor` and the Celery Beat integration pass the returned id into the closing `capture_checkin` call, which generates a new id when it gets `None`, so they behave as before. The return type becomes `Optional[str]`, so type-checked code that uses the result as a `str` will now get a mypy error until it handles `None`. `test_capture_checkin_sdk_not_initialized` expected the passed-in id back when the SDK isn't initialized and now expects `None`. The alternative was keeping the `str` return type and adding a separate way to ask whether the check-in went out, but `None` matches `capture_event` and needs no new API. #### Issues * resolves: #7845 * resolves: PY-2924
1 parent c5f80df commit 6a4eb20

2 files changed

Lines changed: 39 additions & 3 deletions

File tree

‎sentry_sdk/crons/api.py‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def capture_checkin(
4242
status: "Optional[str]" = None,
4343
duration: "Optional[float]" = None,
4444
monitor_config: "Optional[MonitorConfig]" = None,
45-
) -> str:
45+
) -> "Optional[str]":
4646
check_in_event = _create_check_in_event(
4747
monitor_slug=monitor_slug,
4848
check_in_id=check_in_id,
@@ -51,7 +51,8 @@ def capture_checkin(
5151
monitor_config=monitor_config,
5252
)
5353

54-
sentry_sdk.capture_event(check_in_event)
54+
if sentry_sdk.capture_event(check_in_event) is None:
55+
return None
5556

5657
logger.debug(
5758
f"[Crons] Captured check-in ({check_in_event.get('check_in_id')}): {check_in_event.get('monitor_slug')} -> {check_in_event.get('status')}"

‎tests/test_crons.py‎

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,42 @@ def test_capture_checkin_sdk_not_initialized():
321321
status=None,
322322
duration=None,
323323
)
324-
assert check_in_id == "112233"
324+
assert check_in_id is None
325+
326+
327+
def test_capture_checkin_dropped_by_before_send(sentry_init, capture_envelopes):
328+
sentry_init(before_send=lambda event, hint: None)
329+
envelopes = capture_envelopes()
330+
331+
check_in_id = capture_checkin(monitor_slug="abc123", status="in_progress")
332+
333+
assert check_in_id is None
334+
assert envelopes == []
335+
336+
337+
def test_context_monitor_exit_successfully_sends_checkin_when_enter_checkin_dropped(
338+
sentry_init, capture_envelopes
339+
):
340+
sentry_init(
341+
before_send=lambda event, hint: (
342+
None if event.get("status") == "in_progress" else event
343+
)
344+
)
345+
envelopes = capture_envelopes()
346+
347+
monitor = sentry_sdk.monitor(monitor_slug="abc123")
348+
with monitor:
349+
# Confirms that the check in was dropped in the `__enter__`
350+
assert monitor.check_in_id is None
351+
352+
(envelope,) = envelopes
353+
check_in = envelope.items[0].payload.json
354+
355+
# Confirms that the second check in in `__exit__` works correctly
356+
assert check_in["monitor_slug"] == "abc123"
357+
assert check_in["check_in_id"]
358+
assert check_in["status"] == "ok"
359+
assert check_in["duration"] > 0
325360

326361

327362
def test_scope_data_in_checkin(sentry_init, capture_envelopes):

0 commit comments

Comments
 (0)