Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set correct data in check_ins #2500

Merged
merged 18 commits into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions sentry_sdk/_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"internal",
"profile",
"statsd",
"check_in",
]
SessionStatus = Literal["ok", "exited", "crashed", "abnormal"]
EndpointType = Literal["store", "envelope"]
Expand Down
2 changes: 2 additions & 0 deletions sentry_sdk/envelope.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,8 @@
return "profile"
elif ty == "statsd":
return "statsd"
elif ty == "check_in":
return "check_in"

Check warning on line 266 in sentry_sdk/envelope.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/envelope.py#L266

Added line #L266 was not covered by tests
else:
return "default"

Expand Down
110 changes: 77 additions & 33 deletions sentry_sdk/scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -560,69 +560,62 @@

self._error_processors.append(func)

@_disable_capture
def apply_to_event(
self,
event, # type: Event
hint, # type: Hint
options=None, # type: Optional[Dict[str, Any]]
):
# type: (...) -> Optional[Event]
"""Applies the information contained on the scope to the given event."""

def _drop(cause, ty):
# type: (Any, str) -> Optional[Any]
logger.info("%s (%s) dropped event", ty, cause)
return None

is_transaction = event.get("type") == "transaction"

# put all attachments into the hint. This lets callbacks play around
# with attachments. We also later pull this out of the hint when we
# create the envelope.
attachments_to_send = hint.get("attachments") or []
for attachment in self._attachments:
if not is_transaction or attachment.add_to_transactions:
attachments_to_send.append(attachment)
hint["attachments"] = attachments_to_send

def _apply_level_to_event(self, event, hint, options):
# type: (Event, Hint, Optional[Dict[str, Any]]) -> None
if self._level is not None:
event["level"] = self._level

if not is_transaction:
event.setdefault("breadcrumbs", {}).setdefault("values", []).extend(
self._breadcrumbs
)
def _apply_breadcrumbs_to_event(self, event, hint, options):
# type: (Event, Hint, Optional[Dict[str, Any]]) -> None
event.setdefault("breadcrumbs", {}).setdefault("values", []).extend(
self._breadcrumbs
)

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

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

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

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

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

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

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

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

# Add "trace" context
if contexts.get("trace") is None:
if has_tracing_enabled(options) and self._span is not None:
contexts["trace"] = self._span.get_trace_context()
else:
contexts["trace"] = self.get_trace_context()

# Add "reply_id" context
try:
replay_id = contexts["trace"]["dynamic_sampling_context"]["replay_id"]
except (KeyError, TypeError):
Expand All @@ -633,22 +626,73 @@
"replay_id": replay_id,
}

def _run_error_processors(self, event, hint, options):
# type: (Event, Hint, Optional[Dict[str, Any]]) -> None
exc_info = hint.get("exc_info")
if exc_info is not None:
for error_processor in self._error_processors:
new_event = error_processor(event, exc_info)
if new_event is None:
return _drop(error_processor, "error processor")
logger.info("error processor (%s) dropped event", error_processor)
return None

Check warning on line 637 in sentry_sdk/scope.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/scope.py#L636-L637

Added lines #L636 - L637 were not covered by tests

event = new_event

def _run_event_processors(self, event, hint, options):
# type: (Event, Hint, Optional[Dict[str, Any]]) -> None
for event_processor in chain(global_event_processors, self._event_processors):
new_event = event
new_event = event # type: Optional[Event]

Check warning on line 644 in sentry_sdk/scope.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/scope.py#L644

Added line #L644 was not covered by tests
with capture_internal_exceptions():
new_event = event_processor(event, hint)
if new_event is None:
return _drop(event_processor, "event processor")
logger.info("event processor (%s) dropped event", event_processor)
return None

Check warning on line 649 in sentry_sdk/scope.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/scope.py#L648-L649

Added lines #L648 - L649 were not covered by tests
event = new_event

@_disable_capture
def apply_to_event(
self,
event, # type: Event
hint, # type: Hint
options=None, # type: Optional[Dict[str, Any]]
):
# type: (...) -> Optional[Event]
"""Applies the information contained on the scope to the given event."""
ty = event.get("type")
is_transaction = ty == "transaction"
is_check_in = ty == "check_in"

# put all attachments into the hint. This lets callbacks play around
# with attachments. We also later pull this out of the hint when we
# create the envelope.
attachments_to_send = hint.get("attachments") or []
for attachment in self._attachments:
if not is_transaction or attachment.add_to_transactions:
attachments_to_send.append(attachment)

Check warning on line 671 in sentry_sdk/scope.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/scope.py#L671

Added line #L671 was not covered by tests
hint["attachments"] = attachments_to_send

self._apply_level_to_event(event, hint, options)
self._apply_fingerprint_to_event(event, hint, options)
self._apply_user_to_event(event, hint, options)
self._apply_tags_to_event(event, hint, options)
self._apply_contexts_to_event(event, hint, options)
self._apply_transaction_name_to_event(event, hint, options)
antonpirker marked this conversation as resolved.
Show resolved Hide resolved

if not is_check_in:
self._apply_transaction_info_to_event(event, hint, options)
self._apply_extra_to_event(event, hint, options)

if not is_transaction and not is_check_in:
self._apply_breadcrumbs_to_event(event, hint, options)

event = self._run_error_processors(event, hint, options)
if event is None:
return None

event = self._run_event_processors(event, hint, options)

Check warning on line 692 in sentry_sdk/scope.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/scope.py#L692

Added line #L692 was not covered by tests
if event is None:
return None

Check warning on line 694 in sentry_sdk/scope.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/scope.py#L694

Added line #L694 was not covered by tests

return event

def update_from_scope(self, scope):
Expand Down
61 changes: 61 additions & 0 deletions tests/test_crons.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import sentry_sdk
from sentry_sdk.crons import capture_checkin

from sentry_sdk import Hub, configure_scope, set_level

try:
from unittest import mock # python 3.3 and above
except ImportError:
Expand Down Expand Up @@ -220,3 +222,62 @@ def test_capture_checkin_sdk_not_initialized():
duration=None,
)
assert check_in_id == "112233"


def test_scope_data_in_checkin(sentry_init, capture_envelopes):
sentry_init()
envelopes = capture_envelopes()

valid_keys = [
# Mandatory event keys
"type",
"event_id",
"timestamp",
"platform",
antonpirker marked this conversation as resolved.
Show resolved Hide resolved
# Optional event keys
"release",
"environment",
# Mandatory check-in specific keys
"check_in_id",
"monitor_slug",
"status",
# Optional check-in specific keys
"duration",
"monitor_config",
"contexts",
# TODO: These fields need to be checked if valid for checkin:
"level",
"tags",
"extra",
"modules",
"server_name",
"sdk",
antonpirker marked this conversation as resolved.
Show resolved Hide resolved
]

hub = Hub.current
with configure_scope() as scope:
# Add some data to the scope
set_level("warning")
hub.add_breadcrumb(message="test breadcrumb")
scope.set_tag("test_tag", "test_value")
scope.set_extra("test_extra", "test_value")
scope.set_context("test_context", {"test_key": "test_value"})

capture_checkin(
monitor_slug="abc123",
check_in_id="112233",
status="ok",
duration=123,
)

(envelope,) = envelopes
check_in_event = envelope.items[0].payload.json

invalid_keys = []
for key in check_in_event.keys():
if key not in valid_keys:
invalid_keys.append(key)

assert (
len(invalid_keys) == 0
), f"Unexpected keys found in checkin: {invalid_keys}"
Loading