Skip to content

Commit 338acda

Browse files
authored
Set correct data in check_ins (#2500)
Made sure that only relevant data is added to check_ins and breadcrumbs, and other things are not sent with checkins, because checkins have a strict size limit.
1 parent 35d86b6 commit 338acda

File tree

6 files changed

+133
-32
lines changed

6 files changed

+133
-32
lines changed

sentry_sdk/_types.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
"internal",
5555
"profile",
5656
"statsd",
57+
"check_in",
5758
]
5859
SessionStatus = Literal["ok", "exited", "crashed", "abnormal"]
5960
EndpointType = Literal["store", "envelope"]

sentry_sdk/envelope.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,8 @@ def data_category(self):
262262
return "profile"
263263
elif ty == "statsd":
264264
return "statsd"
265+
elif ty == "check_in":
266+
return "check_in"
265267
else:
266268
return "default"
267269

sentry_sdk/integrations/rq.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def sentry_patched_handle_exception(self, job, *exc_info, **kwargs):
9999
# Note, the order of the `or` here is important,
100100
# because calling `job.is_failed` will change `_status`.
101101
if job._status == JobStatus.FAILED or job.is_failed:
102-
_capture_exception(exc_info) # type: ignore
102+
_capture_exception(exc_info)
103103

104104
return old_handle_exception(self, job, *exc_info, **kwargs)
105105

sentry_sdk/scope.py

Lines changed: 67 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -560,69 +560,62 @@ def func(event, exc_info):
560560

561561
self._error_processors.append(func)
562562

563-
@_disable_capture
564-
def apply_to_event(
565-
self,
566-
event, # type: Event
567-
hint, # type: Hint
568-
options=None, # type: Optional[Dict[str, Any]]
569-
):
570-
# type: (...) -> Optional[Event]
571-
"""Applies the information contained on the scope to the given event."""
572-
573-
def _drop(cause, ty):
574-
# type: (Any, str) -> Optional[Any]
575-
logger.info("%s (%s) dropped event", ty, cause)
576-
return None
577-
578-
is_transaction = event.get("type") == "transaction"
579-
580-
# put all attachments into the hint. This lets callbacks play around
581-
# with attachments. We also later pull this out of the hint when we
582-
# create the envelope.
583-
attachments_to_send = hint.get("attachments") or []
584-
for attachment in self._attachments:
585-
if not is_transaction or attachment.add_to_transactions:
586-
attachments_to_send.append(attachment)
587-
hint["attachments"] = attachments_to_send
588-
563+
def _apply_level_to_event(self, event, hint, options):
564+
# type: (Event, Hint, Optional[Dict[str, Any]]) -> None
589565
if self._level is not None:
590566
event["level"] = self._level
591567

592-
if not is_transaction:
593-
event.setdefault("breadcrumbs", {}).setdefault("values", []).extend(
594-
self._breadcrumbs
595-
)
568+
def _apply_breadcrumbs_to_event(self, event, hint, options):
569+
# type: (Event, Hint, Optional[Dict[str, Any]]) -> None
570+
event.setdefault("breadcrumbs", {}).setdefault("values", []).extend(
571+
self._breadcrumbs
572+
)
596573

574+
def _apply_user_to_event(self, event, hint, options):
575+
# type: (Event, Hint, Optional[Dict[str, Any]]) -> None
597576
if event.get("user") is None and self._user is not None:
598577
event["user"] = self._user
599578

579+
def _apply_transaction_name_to_event(self, event, hint, options):
580+
# type: (Event, Hint, Optional[Dict[str, Any]]) -> None
600581
if event.get("transaction") is None and self._transaction is not None:
601582
event["transaction"] = self._transaction
602583

584+
def _apply_transaction_info_to_event(self, event, hint, options):
585+
# type: (Event, Hint, Optional[Dict[str, Any]]) -> None
603586
if event.get("transaction_info") is None and self._transaction_info is not None:
604587
event["transaction_info"] = self._transaction_info
605588

589+
def _apply_fingerprint_to_event(self, event, hint, options):
590+
# type: (Event, Hint, Optional[Dict[str, Any]]) -> None
606591
if event.get("fingerprint") is None and self._fingerprint is not None:
607592
event["fingerprint"] = self._fingerprint
608593

594+
def _apply_extra_to_event(self, event, hint, options):
595+
# type: (Event, Hint, Optional[Dict[str, Any]]) -> None
609596
if self._extras:
610597
event.setdefault("extra", {}).update(self._extras)
611598

599+
def _apply_tags_to_event(self, event, hint, options):
600+
# type: (Event, Hint, Optional[Dict[str, Any]]) -> None
612601
if self._tags:
613602
event.setdefault("tags", {}).update(self._tags)
614603

604+
def _apply_contexts_to_event(self, event, hint, options):
605+
# type: (Event, Hint, Optional[Dict[str, Any]]) -> None
615606
if self._contexts:
616607
event.setdefault("contexts", {}).update(self._contexts)
617608

618609
contexts = event.setdefault("contexts", {})
619610

611+
# Add "trace" context
620612
if contexts.get("trace") is None:
621613
if has_tracing_enabled(options) and self._span is not None:
622614
contexts["trace"] = self._span.get_trace_context()
623615
else:
624616
contexts["trace"] = self.get_trace_context()
625617

618+
# Add "reply_id" context
626619
try:
627620
replay_id = contexts["trace"]["dynamic_sampling_context"]["replay_id"]
628621
except (KeyError, TypeError):
@@ -633,14 +626,58 @@ def _drop(cause, ty):
633626
"replay_id": replay_id,
634627
}
635628

629+
@_disable_capture
630+
def apply_to_event(
631+
self,
632+
event, # type: Event
633+
hint, # type: Hint
634+
options=None, # type: Optional[Dict[str, Any]]
635+
):
636+
# type: (...) -> Optional[Event]
637+
"""Applies the information contained on the scope to the given event."""
638+
ty = event.get("type")
639+
is_transaction = ty == "transaction"
640+
is_check_in = ty == "check_in"
641+
642+
# put all attachments into the hint. This lets callbacks play around
643+
# with attachments. We also later pull this out of the hint when we
644+
# create the envelope.
645+
attachments_to_send = hint.get("attachments") or []
646+
for attachment in self._attachments:
647+
if not is_transaction or attachment.add_to_transactions:
648+
attachments_to_send.append(attachment)
649+
hint["attachments"] = attachments_to_send
650+
651+
self._apply_contexts_to_event(event, hint, options)
652+
653+
if not is_check_in:
654+
self._apply_level_to_event(event, hint, options)
655+
self._apply_fingerprint_to_event(event, hint, options)
656+
self._apply_user_to_event(event, hint, options)
657+
self._apply_transaction_name_to_event(event, hint, options)
658+
self._apply_transaction_info_to_event(event, hint, options)
659+
self._apply_tags_to_event(event, hint, options)
660+
self._apply_extra_to_event(event, hint, options)
661+
662+
if not is_transaction and not is_check_in:
663+
self._apply_breadcrumbs_to_event(event, hint, options)
664+
665+
def _drop(cause, ty):
666+
# type: (Any, str) -> Optional[Any]
667+
logger.info("%s (%s) dropped event", ty, cause)
668+
return None
669+
670+
# run error processors
636671
exc_info = hint.get("exc_info")
637672
if exc_info is not None:
638673
for error_processor in self._error_processors:
639674
new_event = error_processor(event, exc_info)
640675
if new_event is None:
641676
return _drop(error_processor, "error processor")
677+
642678
event = new_event
643679

680+
# run event processors
644681
for event_processor in chain(global_event_processors, self._event_processors):
645682
new_event = event
646683
with capture_internal_exceptions():

sentry_sdk/transport.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -586,7 +586,7 @@ def make_transport(options):
586586
elif isinstance(ref_transport, type) and issubclass(ref_transport, Transport):
587587
transport_cls = ref_transport
588588
elif callable(ref_transport):
589-
return _FunctionTransport(ref_transport) # type: ignore
589+
return _FunctionTransport(ref_transport)
590590

591591
# if a transport class is given only instantiate it if the dsn is not
592592
# empty or None

tests/test_crons.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import sentry_sdk
55
from sentry_sdk.crons import capture_checkin
66

7+
from sentry_sdk import Hub, configure_scope, set_level
8+
79
try:
810
from unittest import mock # python 3.3 and above
911
except ImportError:
@@ -220,3 +222,62 @@ def test_capture_checkin_sdk_not_initialized():
220222
duration=None,
221223
)
222224
assert check_in_id == "112233"
225+
226+
227+
def test_scope_data_in_checkin(sentry_init, capture_envelopes):
228+
sentry_init()
229+
envelopes = capture_envelopes()
230+
231+
valid_keys = [
232+
# Mandatory event keys
233+
"type",
234+
"event_id",
235+
"timestamp",
236+
"platform",
237+
# Optional event keys
238+
"release",
239+
"environment",
240+
# Mandatory check-in specific keys
241+
"check_in_id",
242+
"monitor_slug",
243+
"status",
244+
# Optional check-in specific keys
245+
"duration",
246+
"monitor_config",
247+
"contexts", # an event processor adds this
248+
# TODO: These fields need to be checked if valid for checkin:
249+
"_meta",
250+
"tags",
251+
"extra", # an event processor adds this
252+
"modules",
253+
"server_name",
254+
"sdk",
255+
]
256+
257+
hub = Hub.current
258+
with configure_scope() as scope:
259+
# Add some data to the scope
260+
set_level("warning")
261+
hub.add_breadcrumb(message="test breadcrumb")
262+
scope.set_tag("test_tag", "test_value")
263+
scope.set_extra("test_extra", "test_value")
264+
scope.set_context("test_context", {"test_key": "test_value"})
265+
266+
capture_checkin(
267+
monitor_slug="abc123",
268+
check_in_id="112233",
269+
status="ok",
270+
duration=123,
271+
)
272+
273+
(envelope,) = envelopes
274+
check_in_event = envelope.items[0].payload.json
275+
276+
invalid_keys = []
277+
for key in check_in_event.keys():
278+
if key not in valid_keys:
279+
invalid_keys.append(key)
280+
281+
assert len(invalid_keys) == 0, "Unexpected keys found in checkin: {}".format(
282+
invalid_keys
283+
)

0 commit comments

Comments
 (0)