From f6021c4749876260bca67e07af625970e3e11d19 Mon Sep 17 00:00:00 2001 From: Oliver Date: Fri, 28 Apr 2023 06:54:31 +1000 Subject: [PATCH] sentry.io improvements (#4712) * Write function to catch sentry.io events before sending - Will let us ignore certain types of errors which we are not interested in * Cleanup * Include release info * Allow sentry reporting in debug mode * Consolidate DRF sentry code into InvenTree/sentry.py * Add more error types to ignore * update docs --- InvenTree/InvenTree/exceptions.py | 20 ++++----- InvenTree/InvenTree/sentry.py | 68 +++++++++++++++++++++++++++++++ InvenTree/InvenTree/settings.py | 24 ++++------- docs/docs/settings/logs.md | 4 ++ 4 files changed, 88 insertions(+), 28 deletions(-) create mode 100644 InvenTree/InvenTree/sentry.py diff --git a/InvenTree/InvenTree/exceptions.py b/InvenTree/InvenTree/exceptions.py index 09fc4de192..63321ac69c 100644 --- a/InvenTree/InvenTree/exceptions.py +++ b/InvenTree/InvenTree/exceptions.py @@ -18,6 +18,8 @@ from rest_framework import serializers from rest_framework.exceptions import ValidationError as DRFValidationError from rest_framework.response import Response +import InvenTree.sentry + logger = logging.getLogger('inventree') @@ -61,18 +63,12 @@ def exception_handler(exc, context): """ response = None - if settings.SENTRY_ENABLED and settings.SENTRY_DSN and not settings.DEBUG: - # Report this exception to sentry.io - from sentry_sdk import capture_exception - - # The following types of errors are ignored, they are "expected" - do_not_report = [ - DjangoValidationError, - DRFValidationError, - ] - - if not any([isinstance(exc, err) for err in do_not_report]): - capture_exception(exc) + # Pass exception to sentry.io handler + try: + InvenTree.sentry.report_exception(exc) + except Exception: + # If sentry.io fails, we don't want to crash the server! + pass # Catch any django validation error, and re-throw a DRF validation error if isinstance(exc, DjangoValidationError): diff --git a/InvenTree/InvenTree/sentry.py b/InvenTree/InvenTree/sentry.py new file mode 100644 index 0000000000..471624ac4a --- /dev/null +++ b/InvenTree/InvenTree/sentry.py @@ -0,0 +1,68 @@ +"""Configuration for Sentry.io error reporting.""" + +import logging + +from django.conf import settings +from django.core.exceptions import ValidationError +from django.http import Http404 + +import rest_framework.exceptions +import sentry_sdk +from sentry_sdk.integrations.django import DjangoIntegration + +from InvenTree.version import INVENTREE_SW_VERSION + +logger = logging.getLogger('inventree') + + +def default_sentry_dsn(): + """Return the default Sentry.io DSN for InvenTree""" + + return 'https://3928ccdba1d34895abde28031fd00100@o378676.ingest.sentry.io/6494600' + + +def sentry_ignore_errors(): + """Return a list of error types to ignore. + + These error types will *not* be reported to sentry.io. + """ + + return [ + Http404, + ValidationError, + rest_framework.exceptions.AuthenticationFailed, + rest_framework.exceptions.PermissionDenied, + rest_framework.exceptions.ValidationError, + ] + + +def init_sentry(dsn, sample_rate, tags): + """Initialize sentry.io error reporting""" + + logger.info("Initializing sentry.io integration") + + sentry_sdk.init( + dsn=dsn, + integrations=[DjangoIntegration()], + traces_sample_rate=sample_rate, + send_default_pii=True, + ignore_errors=sentry_ignore_errors(), + release=INVENTREE_SW_VERSION, + ) + + for key, val in tags.items(): + sentry_sdk.set_tag(f'inventree_{key}', val) + + +def report_exception(exc): + """Report an exception to sentry.io""" + + if settings.SENTRY_ENABLED and settings.SENTRY_DSN: + + if not any([isinstance(exc, e) for e in sentry_ignore_errors()]): + logger.info(f"Reporting exception to sentry.io: {exc}") + + try: + sentry_sdk.capture_exception(exc) + except Exception: + logger.warning("Failed to report exception to sentry.io") diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py index 0b4421c4e0..f820c8aa2d 100644 --- a/InvenTree/InvenTree/settings.py +++ b/InvenTree/InvenTree/settings.py @@ -21,12 +21,12 @@ from django.http import Http404 from django.utils.translation import gettext_lazy as _ import moneyed -import sentry_sdk -from sentry_sdk.integrations.django import DjangoIntegration + +from InvenTree.config import get_boolean_setting, get_custom_file, get_setting +from InvenTree.sentry import default_sentry_dsn, init_sentry +from InvenTree.version import inventreeApiVersion from . import config -from .config import get_boolean_setting, get_custom_file, get_setting -from .version import inventreeApiVersion INVENTREE_NEWS_URL = 'https://inventree.org/news/feed.atom' @@ -573,29 +573,21 @@ REMOTE_LOGIN_HEADER = get_setting('INVENTREE_REMOTE_LOGIN_HEADER', 'remote_login # sentry.io integration for error reporting SENTRY_ENABLED = get_boolean_setting('INVENTREE_SENTRY_ENABLED', 'sentry_enabled', False) + # Default Sentry DSN (can be overriden if user wants custom sentry integration) -INVENTREE_DSN = 'https://3928ccdba1d34895abde28031fd00100@o378676.ingest.sentry.io/6494600' -SENTRY_DSN = get_setting('INVENTREE_SENTRY_DSN', 'sentry_dsn', INVENTREE_DSN) +SENTRY_DSN = get_setting('INVENTREE_SENTRY_DSN', 'sentry_dsn', default_sentry_dsn()) SENTRY_SAMPLE_RATE = float(get_setting('INVENTREE_SENTRY_SAMPLE_RATE', 'sentry_sample_rate', 0.1)) if SENTRY_ENABLED and SENTRY_DSN: # pragma: no cover - logger.info("Running with sentry.io integration enabled") - - sentry_sdk.init( - dsn=SENTRY_DSN, - integrations=[DjangoIntegration(), ], - traces_sample_rate=1.0 if DEBUG else SENTRY_SAMPLE_RATE, - send_default_pii=True - ) inventree_tags = { 'testing': TESTING, 'docker': DOCKER, 'debug': DEBUG, 'remote': REMOTE_LOGIN, } - for key, val in inventree_tags.items(): - sentry_sdk.set_tag(f'inventree_{key}', val) + + init_sentry(SENTRY_DSN, SENTRY_SAMPLE_RATE, inventree_tags) # Cache configuration cache_host = get_setting('INVENTREE_CACHE_HOST', 'cache.host', None) diff --git a/docs/docs/settings/logs.md b/docs/docs/settings/logs.md index 95564cde93..c81a7dece7 100644 --- a/docs/docs/settings/logs.md +++ b/docs/docs/settings/logs.md @@ -27,3 +27,7 @@ A list of error logs is presented. ## Reporting Errors Errors should be reported to the [InvenTree GitHub page](https://github.com/inventree/inventree/issues), and include the full error output as recorded to the error log. + +### Sentry Integration + +If [sentry.io integration](../start/config.md#sentry-integration) is enabled, some error logs are automatically reported to sentry.io