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

Enable sending of breadcrumb data #138

Merged
merged 2 commits into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ Do not forget to add the `structlog.stdlib.add_log_level` and optionally the
as events. Default is `logging.WARNING`.
- `active` A flag to make this processor enabled/disabled.
- `as_context` Send `event_dict` as extra info to Sentry. Default is `True`.
- `ignore_breadcrumb_data` A list of data keys that will be excluded from
[breadcrumb data](https://docs.sentry.io/platforms/python/enriching-events/breadcrumbs/#manual-breadcrumbs).
Defaults to keys which are already sent separately, i.e. `level`, `logger`,
`event` and `timestamp`. All other data in `event_dict` will be sent as
breadcrumb data.
- `tag_keys` A list of keys. If any if these keys appear in `event_dict`,
the key and its corresponding value in `event_dict` will be used as Sentry
event tags. use `"__all__"` to report all key/value pairs of event as tags.
Expand Down
14 changes: 13 additions & 1 deletion structlog_sentry/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ def __init__(
event_level: int = logging.WARNING,
active: bool = True,
as_context: bool = True,
ignore_breadcrumb_data: Iterable[str] = (
"level",
"logger",
"event",
"timestamp",
),
tag_keys: list[str] | str | None = None,
ignore_loggers: Iterable[str] | None = None,
verbose: bool = False,
Expand All @@ -51,6 +57,8 @@ def __init__(
:param active: A flag to make this processor enabled/disabled.
:param as_context: Send `event_dict` as extra info to Sentry.
Default is :obj:`True`.
:param ignore_breadcrumb_data: A list of data keys that will be excluded from
breadcrumb data. Defaults to keys which are already sent separately.
:param tag_keys: A list of keys. If any if these keys appear in `event_dict`,
the key and its corresponding value in `event_dict` will be used as Sentry
event tags. use `"__all__"` to report all key/value pairs of event as tags.
Expand All @@ -68,6 +76,7 @@ def __init__(
self._hub = hub
self._as_context = as_context
self._original_event_dict: dict = {}
self.ignore_breadcrumb_data = ignore_breadcrumb_data

self._ignored_loggers: set[str] = set()
if ignore_loggers is not None:
Expand Down Expand Up @@ -131,13 +140,16 @@ def _get_event_and_hint(self, event_dict: EventDict) -> tuple[dict, dict]:
return event, hint

def _get_breadcrumb_and_hint(self, event_dict: EventDict) -> tuple[dict, dict]:
data = {
k: v for k, v in event_dict.items() if k not in self.ignore_breadcrumb_data
}
event = {
"type": "log",
"level": event_dict.get("level"), # type: ignore
"category": event_dict.get("logger"),
"message": event_dict["event"],
"timestamp": event_dict.get("timestamp"),
"data": {},
"data": data,
}

return event, {"log_record": event_dict}
Expand Down
70 changes: 69 additions & 1 deletion test/test_sentry_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
LoggingIntegration(event_level=None, level=None),
]


# Register custom log level
CUSTOM_LOG_LEVEL_NAME = "CUSTOM_LEVEL"
CUSTOM_LOG_LEVEL_VALUE = logging.DEBUG
Expand Down Expand Up @@ -302,3 +301,72 @@ def test_sentry_json_respects_global_with_locals_option_with_locals(sentry_event
for event in sentry_events:
for frame in event["exception"]["values"][0]["stacktrace"]["frames"]:
assert "vars" in frame # Local variables were captured


base_info_log = {
"level": "info",
"event": "Info message",
"logger": "EventLogger",
"timestamp": "2024-01-01T00:00:00Z",
}
base_error_log = {
"level": "error",
"event": "Error message",
}


def test_breadcrumbs_with_additional_data(sentry_events):
processor = SentryProcessor(verbose=True)
processor(None, None, {**base_info_log, **{"foo": "bar"}})
processor(None, None, base_error_log)
print({**base_info_log, **{"foo": "bar"}})
breadcrumbs = sentry_events[0]["breadcrumbs"]["values"]
del breadcrumbs[0]["timestamp"]
assert breadcrumbs[0] == {
"type": "log",
"level": "info",
"category": "EventLogger",
"message": "Info message",
"data": {"foo": "bar"},
}


def test_breadcrumbs_with_custom_exclusions(sentry_events):
processor = SentryProcessor(verbose=True, ignore_breadcrumb_data=["foo"])
processor(None, None, {**base_info_log, **{"foo": "bar"}})
processor(None, None, base_error_log)

breadcrumbs = sentry_events[0]["breadcrumbs"]["values"]

del breadcrumbs[0]["timestamp"]
assert breadcrumbs[0] == {
"type": "log",
"level": "info",
"category": "EventLogger",
"message": "Info message",
"data": {
"level": "info",
"event": "Info message",
"logger": "EventLogger",
"timestamp": "2024-01-01T00:00:00Z",
},
}


def test_breadcrumbs_with_no_additional_data(sentry_events):
processor = SentryProcessor(verbose=True)
processor(None, None, base_info_log)
processor(None, None, base_error_log)

breadcrumbs = sentry_events[0]["breadcrumbs"]["values"]

assert len(breadcrumbs) == 1
assert isinstance(breadcrumbs[0]["timestamp"], str)
del breadcrumbs[0]["timestamp"]
assert breadcrumbs[0] == {
"type": "log",
"level": "info",
"category": "EventLogger",
"message": "Info message",
"data": {},
}
Loading