Source code for zsl.contrib.sentry.sentry_module

"""
:mod:`zsl.contrib.modules.sentry_module`
----------------------------------------

Sentry module is responsible for handling errors and sending them to a sentry
backend. Just provide a DSN (url) of the backend and you are all set up.

To add the Sentry support just add `SentryModule` to your container.

.. code-block:: python

    class MyApplicationContainer(WebContainer):
        sentry = SentryModule

Configuration
~~~~~~~~~~~~~

To configure one needs to provide a valid `SentryConfiguration` usually in
`default_settings.py` or in your profiles.

.. code-block:: python

    SENTRY = SentryConfiguration(dsn="...")

The following options are available:

    1. `dsn`: DSN/URl of your Sentry project,
    2. `environment`: the environment (release defaults to the version of your application),
    3. `tags`: additional tags str to str dictionary,
    4. `register_sentry_logging_handler`: bool value indicating if logging handler should be added to logging,
    5. `sentry_handler_logging_level`: minimal log level of log messages sent to sentry logging handler.

To test the functionality use CLI

.. code-block:: bash

    python app.py sentry test

"""
import logging

from injector import Binder, provides, singleton
from sentry_sdk import HttpTransport
from sentry_sdk.integrations.logging import LoggingIntegration

from zsl import Config, Module, Zsl, inject
from zsl.application.error_handler import register
from zsl.application.modules.cli_module import ZslCli
from zsl.contrib.sentry.sentry_config import SentryConfiguration
from zsl.errors import ErrorProcessor, ZslError
from zsl.utils.injection_helper import simple_bind

try:
    import sentry_sdk
except ImportError:
    CommandLine = None
    logging.getLogger(__name__).exception(
        "Can not import sentry sdk. Please install it first `pip install zsl [sentry-sdk]`.")
    raise


[docs] class SentryCli: pass """Sentry CLI interface support.""" @inject(zsl_cli=ZslCli) def __init__(self, zsl_cli): # type: (ZslCli) -> None logging.getLogger(__name__).debug("Creating Sentry CLI.") @zsl_cli.cli.group() def sentry(): pass @sentry.command(help='Send a test error to the Sentry backend.') def test(): print('Sending test Sentry message.') raise ZslError("Sentry test error from Zsl.") self._sentry = sentry @property def sentry(self): return self._sentry
[docs] class SentryErrorProcessor(ErrorProcessor): @inject(config=SentryConfiguration, zsl=Zsl) def __init__(self, config, zsl): # type: (SentryConfiguration, Zsl)->None self._init_sdk(config, zsl) @staticmethod def _init_sdk(config, zsl): # type: (SentryConfiguration, Zsl)->None logging_integration = SentryErrorProcessor._register_logging_handler(config) sentry_sdk.init( dsn=config.dsn, transport=HttpTransport, environment=config.environment, release=zsl.get_version(), integrations=[logging_integration] if logging_integration else None, ) for key, value in config.tags.items(): sentry_sdk.set_tag(key, value) @staticmethod def _register_logging_handler(config): # type: (SentryConfiguration)->LoggingIntegration return LoggingIntegration( level=None, event_level=config.sentry_logging_handler_level if config.register_logging_handler else None, )
[docs] def handle(self, e): logging.getLogger(__name__).info('Sending error message for {0}.'.format(e)) sentry_sdk.capture_exception(e)
[docs] class SentryModule(Module): """Adds Sentry support.""" SENTRY_CONFIG_NAME = 'SENTRY'
[docs] @provides(SentryConfiguration) @inject(config=Config) def provide_sentry_configuration(self, config): # type: (Config) -> SentryConfiguration return config.get(SentryModule.SENTRY_CONFIG_NAME)
[docs] def configure(self, binder): # type: (Binder) -> None simple_bind(binder, SentryCli, singleton) register(SentryErrorProcessor())