-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4 from mbdevpl/feature/sentry
add Sentry boilerplate
- Loading branch information
Showing
8 changed files
with
163 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
"""Boilerplate for integrating Sentry into the project.""" | ||
|
||
import logging | ||
import os | ||
import typing as t | ||
|
||
import sentry_sdk | ||
import sentry_sdk.integrations | ||
import sentry_sdk.integrations.argv | ||
import sentry_sdk.integrations.excepthook | ||
import sentry_sdk.integrations.logging | ||
import sentry_sdk.integrations.modules | ||
import sentry_sdk.integrations.pure_eval | ||
import sentry_sdk.integrations.stdlib | ||
import sentry_sdk.integrations.threading | ||
|
||
_LOG = logging.getLogger(__name__) | ||
|
||
|
||
class Sentry: | ||
"""Sentry configuration. | ||
For each parameter, the value is taken from the environment variable if it is set, otherwise | ||
from the class attribute if it is set. | ||
For each parameter, the name of the environment variable name is 'SENTRY_' | ||
followed by the parameter name in upper case. | ||
""" | ||
|
||
dsn: str | ||
release: str | ||
environment: str | ||
integrations: t.List[sentry_sdk.integrations.Integration] = [ | ||
sentry_sdk.integrations.argv.ArgvIntegration(), | ||
sentry_sdk.integrations.excepthook.ExcepthookIntegration(always_run=False), | ||
sentry_sdk.integrations.logging.LoggingIntegration( | ||
level=logging.INFO, event_level=logging.ERROR), | ||
sentry_sdk.integrations.modules.ModulesIntegration(), | ||
sentry_sdk.integrations.pure_eval.PureEvalIntegration(), | ||
sentry_sdk.integrations.stdlib.StdlibIntegration(), | ||
sentry_sdk.integrations.threading.ThreadingIntegration() | ||
] | ||
|
||
traces_sample_rate: float = 1.0 | ||
profiles_sample_rate: float = 1.0 | ||
|
||
@classmethod | ||
def _get_str_param(cls, param_name: str) -> t.Optional[str]: | ||
"""Get a string parameter value by checking envvar first. | ||
Works only for 'dsn', 'release' or 'environment' parameters. | ||
""" | ||
assert param_name in ('dsn', 'release', 'environment'), param_name | ||
return os.environ.get(f'SENTRY_{param_name.upper()}', getattr(cls, param_name, None)) | ||
|
||
@classmethod | ||
def is_dsn_set(cls) -> bool: | ||
"""Check if Sentry DSN parameter is set, thus if Sentry SDK should be initialised or not.""" | ||
dsn = cls._get_str_param('dsn') | ||
return dsn is not None and len(dsn) > 0 | ||
|
||
@classmethod | ||
def init(cls, *args, **kwargs): | ||
"""Initialise Sentry SDK.""" | ||
if not cls.is_dsn_set(): | ||
_LOG.info('Sentry DSN is not set, skipping Sentry SDK initialisation') | ||
return | ||
sentry_sdk.init( | ||
*args, dsn=cls._get_str_param('dsn'), | ||
release=cls._get_str_param('release'), environment=cls._get_str_param('environment'), | ||
integrations=cls.integrations, | ||
traces_sample_rate=cls.traces_sample_rate, | ||
profiles_sample_rate=cls.profiles_sample_rate, | ||
enable_tracing=cls.profiles_sample_rate > 0, | ||
**kwargs) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
sentry-sdk[pure_eval] ~= 1.40 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
"""Unit tests for Sentry boilerplate.""" | ||
|
||
import logging | ||
import sys | ||
import unittest | ||
import unittest.mock | ||
|
||
import boilerplates.sentry | ||
|
||
|
||
class SentryTests(unittest.TestCase): | ||
|
||
def test_init(self): | ||
with self.assertLogs('boilerplates.sentry', logging.INFO) as context: | ||
boilerplates.sentry.Sentry.init() | ||
self.assertEqual(len(context.output), 1, msg=context.output) | ||
self.assertIn('skipping Sentry SDK initialisation', context.output[0]) | ||
|
||
def test_init_without_dsn(self): | ||
class Sentry(boilerplates.sentry.Sentry): | ||
dsn = '' | ||
with self.assertLogs('boilerplates.sentry', logging.INFO) as context, \ | ||
unittest.mock.patch('sentry_sdk.init') as sentry_sdk_init_mock: | ||
Sentry.init() | ||
sentry_sdk_init_mock.assert_not_called() | ||
self.assertEqual(len(context.output), 1, msg=context.output) | ||
self.assertIn('skipping Sentry SDK initialisation', context.output[0]) | ||
|
||
@unittest.skipUnless(sys.version_info >= (3, 10), 'this test requires Python 3.10') | ||
def test_init_with_dsn(self): | ||
class Sentry(boilerplates.sentry.Sentry): | ||
dsn = 'https://spam@ham.ingest.sentry.io/eggs' | ||
with self.assertNoLogs('boilerplates.sentry', logging.INFO), \ | ||
unittest.mock.patch('sentry_sdk.init') as sentry_sdk_init_mock: | ||
Sentry.init() | ||
sentry_sdk_init_mock.assert_called_once() |