mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-11-04 15:15:42 +00:00 
			
		
		
		
	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
This commit is contained in:
		@@ -18,6 +18,8 @@ from rest_framework import serializers
 | 
				
			|||||||
from rest_framework.exceptions import ValidationError as DRFValidationError
 | 
					from rest_framework.exceptions import ValidationError as DRFValidationError
 | 
				
			||||||
from rest_framework.response import Response
 | 
					from rest_framework.response import Response
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import InvenTree.sentry
 | 
				
			||||||
 | 
					
 | 
				
			||||||
logger = logging.getLogger('inventree')
 | 
					logger = logging.getLogger('inventree')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -61,18 +63,12 @@ def exception_handler(exc, context):
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
    response = None
 | 
					    response = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if settings.SENTRY_ENABLED and settings.SENTRY_DSN and not settings.DEBUG:
 | 
					    # Pass exception to sentry.io handler
 | 
				
			||||||
        # Report this exception to sentry.io
 | 
					    try:
 | 
				
			||||||
        from sentry_sdk import capture_exception
 | 
					        InvenTree.sentry.report_exception(exc)
 | 
				
			||||||
 | 
					    except Exception:
 | 
				
			||||||
        # The following types of errors are ignored, they are "expected"
 | 
					        # If sentry.io fails, we don't want to crash the server!
 | 
				
			||||||
        do_not_report = [
 | 
					        pass
 | 
				
			||||||
            DjangoValidationError,
 | 
					 | 
				
			||||||
            DRFValidationError,
 | 
					 | 
				
			||||||
        ]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if not any([isinstance(exc, err) for err in do_not_report]):
 | 
					 | 
				
			||||||
            capture_exception(exc)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Catch any django validation error, and re-throw a DRF validation error
 | 
					    # Catch any django validation error, and re-throw a DRF validation error
 | 
				
			||||||
    if isinstance(exc, DjangoValidationError):
 | 
					    if isinstance(exc, DjangoValidationError):
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										68
									
								
								InvenTree/InvenTree/sentry.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								InvenTree/InvenTree/sentry.py
									
									
									
									
									
										Normal file
									
								
							@@ -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")
 | 
				
			||||||
@@ -21,12 +21,12 @@ from django.http import Http404
 | 
				
			|||||||
from django.utils.translation import gettext_lazy as _
 | 
					from django.utils.translation import gettext_lazy as _
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import moneyed
 | 
					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 . 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'
 | 
					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.io integration for error reporting
 | 
				
			||||||
SENTRY_ENABLED = get_boolean_setting('INVENTREE_SENTRY_ENABLED', 'sentry_enabled', False)
 | 
					SENTRY_ENABLED = get_boolean_setting('INVENTREE_SENTRY_ENABLED', 'sentry_enabled', False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Default Sentry DSN (can be overriden if user wants custom sentry integration)
 | 
					# 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', default_sentry_dsn())
 | 
				
			||||||
SENTRY_DSN = get_setting('INVENTREE_SENTRY_DSN', 'sentry_dsn', INVENTREE_DSN)
 | 
					 | 
				
			||||||
SENTRY_SAMPLE_RATE = float(get_setting('INVENTREE_SENTRY_SAMPLE_RATE', 'sentry_sample_rate', 0.1))
 | 
					SENTRY_SAMPLE_RATE = float(get_setting('INVENTREE_SENTRY_SAMPLE_RATE', 'sentry_sample_rate', 0.1))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if SENTRY_ENABLED and SENTRY_DSN:  # pragma: no cover
 | 
					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 = {
 | 
					    inventree_tags = {
 | 
				
			||||||
        'testing': TESTING,
 | 
					        'testing': TESTING,
 | 
				
			||||||
        'docker': DOCKER,
 | 
					        'docker': DOCKER,
 | 
				
			||||||
        'debug': DEBUG,
 | 
					        'debug': DEBUG,
 | 
				
			||||||
        'remote': REMOTE_LOGIN,
 | 
					        '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 configuration
 | 
				
			||||||
cache_host = get_setting('INVENTREE_CACHE_HOST', 'cache.host', None)
 | 
					cache_host = get_setting('INVENTREE_CACHE_HOST', 'cache.host', None)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,3 +27,7 @@ A list of error logs is presented.
 | 
				
			|||||||
## Reporting Errors
 | 
					## 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.
 | 
					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
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user