From f6c0ba2e9ca160deb1268032820e9e5c47a18451 Mon Sep 17 00:00:00 2001 From: Teo Zosa Date: Thu, 9 Sep 2021 17:28:15 -0700 Subject: [PATCH 1/5] :sparkles: Mark package as PEP-561 compatible see: https://www.python.org/dev/peps/pep-0561/ --- pyproject.toml | 2 +- structlog_sentry/py.typed | 0 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 structlog_sentry/py.typed diff --git a/pyproject.toml b/pyproject.toml index 56968dc..8d16b32 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ classifiers=[ "Programming Language :: Python :: 3.9", "Topic :: Software Development :: Libraries :: Python Modules", ] -include = ["*.md", "*.toml", "*.txt", "*.yml", "*.yaml", ".coveragerc", "tox.ini"] +include = ["*.md", "*.toml", "*.txt", "*.yml", "*.yaml", ".coveragerc", "tox.ini", "structlog_sentry/py.typed"] [tool.poetry.dependencies] python = "^3.6" diff --git a/structlog_sentry/py.typed b/structlog_sentry/py.typed new file mode 100644 index 0000000..e69de29 From 80a8746a34c66a20eef6de7ef2e3abd174e4d212 Mon Sep 17 00:00:00 2001 From: Teo Zosa Date: Fri, 10 Sep 2021 11:41:57 -0700 Subject: [PATCH 2/5] :rotating_light: Auto-format with isort --- structlog_sentry/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/structlog_sentry/__init__.py b/structlog_sentry/__init__.py index a9cc566..83eedb0 100644 --- a/structlog_sentry/__init__.py +++ b/structlog_sentry/__init__.py @@ -1,9 +1,11 @@ import logging import sys -from typing import List, Optional, Tuple, Union, Set, Iterable +from typing import Iterable, List, Optional, Set, Tuple, Union from sentry_sdk import capture_event -from sentry_sdk.integrations.logging import ignore_logger as logging_int_ignore_logger +from sentry_sdk.integrations.logging import ( + ignore_logger as logging_int_ignore_logger, +) from sentry_sdk.utils import event_from_exception From ac3fe0d36833af2b333414dfdaab08f05e13169a Mon Sep 17 00:00:00 2001 From: Teo Zosa Date: Fri, 10 Sep 2021 11:42:13 -0700 Subject: [PATCH 3/5] :rotating_light: Auto-format with Black --- test/test_sentry_processor.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/test_sentry_processor.py b/test/test_sentry_processor.py index 9dcd661..6687ee4 100644 --- a/test/test_sentry_processor.py +++ b/test/test_sentry_processor.py @@ -282,7 +282,11 @@ def test_sentry_ignore_logger(mocker, level): ) m_capture_event.assert_called_once_with( - {"level": level, "message": event_data["event"], "extra": sentry_event_data,}, + { + "level": level, + "message": event_data["event"], + "extra": sentry_event_data, + }, hint=None, ) assert blacklisted_logger_event_dict.get("sentry") == "ignored" From e019a566fc50aa73ce8d9f4e9343c825c88d598b Mon Sep 17 00:00:00 2001 From: Teo Zosa Date: Fri, 10 Sep 2021 11:44:42 -0700 Subject: [PATCH 4/5] :label: Add/update type annotations Based on feedback from Mypy. --- structlog_sentry/__init__.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/structlog_sentry/__init__.py b/structlog_sentry/__init__.py index 83eedb0..3a3d17d 100644 --- a/structlog_sentry/__init__.py +++ b/structlog_sentry/__init__.py @@ -1,6 +1,6 @@ import logging import sys -from typing import Iterable, List, Optional, Set, Tuple, Union +from typing import Any, Dict, Iterable, List, Optional, Set, Tuple, Union from sentry_sdk import capture_event from sentry_sdk.integrations.logging import ( @@ -36,13 +36,13 @@ def __init__( self.active = active self.tag_keys = tag_keys self._as_extra = as_extra - self._original_event_dict = None + self._original_event_dict: dict = None self._ignored_loggers: Set[str] = set() if ignore_loggers is not None: self._ignored_loggers.update(set(ignore_loggers)) @staticmethod - def _get_logger_name(logger, event_dict: dict) -> Optional[str]: + def _get_logger_name(logger: Any, event_dict: dict) -> Optional[str]: """Get logger name from event_dict with a fallbacks to logger.name and record.name :param logger: logger instance @@ -62,7 +62,9 @@ def _get_logger_name(logger, event_dict: dict) -> Optional[str]: return logger_name - def _get_event_and_hint(self, event_dict: dict) -> Tuple[dict, Optional[str]]: + def _get_event_and_hint( + self, event_dict: dict + ) -> Tuple[dict, Optional[Dict[str, Any]]]: """Create a sentry event and hint from structlog `event_dict` and sys.exc_info. :param event_dict: structlog event_dict @@ -73,6 +75,7 @@ def _get_event_and_hint(self, event_dict: dict) -> Tuple[dict, Optional[str]]: exc_info = sys.exc_info() has_exc_info = exc_info and exc_info != (None, None, None) + hint: Optional[Dict[str, Any]] if has_exc_info: event, hint = event_from_exception(exc_info) else: @@ -94,7 +97,7 @@ def _get_event_and_hint(self, event_dict: dict) -> Tuple[dict, Optional[str]]: return event, hint - def _log(self, event_dict: dict) -> str: + def _log(self, event_dict: dict) -> Optional[str]: """Send an event to Sentry and return sentry event id. :param event_dict: structlog event_dict @@ -102,7 +105,7 @@ def _log(self, event_dict: dict) -> str: event, hint = self._get_event_and_hint(event_dict) return capture_event(event, hint=hint) - def __call__(self, logger, method, event_dict) -> dict: + def __call__(self, logger: Any, method: Any, event_dict: dict) -> dict: """A middleware to process structlog `event_dict` and send it to Sentry.""" logger_name = self._get_logger_name(logger=logger, event_dict=event_dict) if logger_name in self._ignored_loggers: @@ -130,19 +133,19 @@ class SentryJsonProcessor(SentryProcessor): Uses Sentry SDK to capture events in Sentry. """ - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) # A set of all encountered structured logger names. If an application uses # multiple loggers with different names (eg. different qualnames), then each of # those loggers needs to be ignored in Sentry's logging integration so that this # processor will be the only thing reporting the events. - self._ignored = set() + self._ignored: set = set() - def __call__(self, logger, method, event_dict) -> dict: + def __call__(self, logger: Any, method: Any, event_dict: dict) -> dict: self._ignore_logger(logger, event_dict) return super().__call__(logger, method, event_dict) - def _ignore_logger(self, logger, event_dict: dict) -> None: + def _ignore_logger(self, logger: Any, event_dict: dict) -> None: """Tell Sentry to ignore logger, if we haven't already. This is temporary workaround to prevent duplication of a JSON event in Sentry. From 070b9402babba6489d73d5c5ffa58e5d5360c601 Mon Sep 17 00:00:00 2001 From: Teo Zosa Date: Sat, 11 Sep 2021 17:07:13 -0700 Subject: [PATCH 5/5] :construction: [APPROVAL NEEDED] Explicitly specify class constructor parameters Resolves `TypeError`s thrown by Mypyc-compiled code as a result of empty tuple assignment to an int-typed `level` parameter during superclass initialization. --- structlog_sentry/__init__.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/structlog_sentry/__init__.py b/structlog_sentry/__init__.py index 3a3d17d..cc99e39 100644 --- a/structlog_sentry/__init__.py +++ b/structlog_sentry/__init__.py @@ -133,8 +133,16 @@ class SentryJsonProcessor(SentryProcessor): Uses Sentry SDK to capture events in Sentry. """ - def __init__(self, *args: Any, **kwargs: Any) -> None: - super().__init__(*args, **kwargs) + def __init__( + self, + level: int = logging.WARNING, + active: bool = True, + as_extra: bool = True, + tag_keys: Union[List[str], str] = None, + ) -> None: + super().__init__( + level=level, active=active, as_extra=as_extra, tag_keys=tag_keys + ) # A set of all encountered structured logger names. If an application uses # multiple loggers with different names (eg. different qualnames), then each of # those loggers needs to be ignored in Sentry's logging integration so that this