234 lines
8.3 KiB
Python
234 lines
8.3 KiB
Python
"""
|
|
raven.contrib.django.models
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Acts as an implicit hook for Django installs.
|
|
|
|
:copyright: (c) 2010-2012 by the Sentry Team, see AUTHORS for more details.
|
|
:license: BSD, see LICENSE for more details.
|
|
"""
|
|
|
|
from __future__ import absolute_import
|
|
from __future__ import unicode_literals
|
|
|
|
from hashlib import md5
|
|
import logging
|
|
import sys
|
|
import warnings
|
|
|
|
from raven.utils import six
|
|
|
|
from django.conf import settings as django_settings
|
|
|
|
logger = logging.getLogger('sentry.errors.client')
|
|
|
|
|
|
def get_installed_apps():
|
|
"""
|
|
Modules in settings.INSTALLED_APPS as a set.
|
|
"""
|
|
return set(django_settings.INSTALLED_APPS)
|
|
|
|
|
|
_client = (None, None)
|
|
|
|
|
|
class ProxyClient(object):
|
|
"""
|
|
A proxy which represents the currently client at all times.
|
|
"""
|
|
# introspection support:
|
|
__members__ = property(lambda x: x.__dir__())
|
|
|
|
# Need to pretend to be the wrapped class, for the sake of objects that care
|
|
# about this (especially in equality tests)
|
|
__class__ = property(lambda x: get_client().__class__)
|
|
|
|
__dict__ = property(lambda o: get_client().__dict__)
|
|
|
|
__repr__ = lambda x: repr(get_client())
|
|
__getattr__ = lambda x, o: getattr(get_client(), o)
|
|
__setattr__ = lambda x, o, v: setattr(get_client(), o, v)
|
|
__delattr__ = lambda x, o: delattr(get_client(), o)
|
|
|
|
__lt__ = lambda x, o: get_client() < o
|
|
__le__ = lambda x, o: get_client() <= o
|
|
__eq__ = lambda x, o: get_client() == o
|
|
__ne__ = lambda x, o: get_client() != o
|
|
__gt__ = lambda x, o: get_client() > o
|
|
__ge__ = lambda x, o: get_client() >= o
|
|
if not six.PY3:
|
|
__cmp__ = lambda x, o: cmp(get_client(), o) # NOQA
|
|
__hash__ = lambda x: hash(get_client())
|
|
# attributes are currently not callable
|
|
# __call__ = lambda x, *a, **kw: get_client()(*a, **kw)
|
|
__nonzero__ = lambda x: bool(get_client())
|
|
__len__ = lambda x: len(get_client())
|
|
__getitem__ = lambda x, i: get_client()[i]
|
|
__iter__ = lambda x: iter(get_client())
|
|
__contains__ = lambda x, i: i in get_client()
|
|
__getslice__ = lambda x, i, j: get_client()[i:j]
|
|
__add__ = lambda x, o: get_client() + o
|
|
__sub__ = lambda x, o: get_client() - o
|
|
__mul__ = lambda x, o: get_client() * o
|
|
__floordiv__ = lambda x, o: get_client() // o
|
|
__mod__ = lambda x, o: get_client() % o
|
|
__divmod__ = lambda x, o: get_client().__divmod__(o)
|
|
__pow__ = lambda x, o: get_client() ** o
|
|
__lshift__ = lambda x, o: get_client() << o
|
|
__rshift__ = lambda x, o: get_client() >> o
|
|
__and__ = lambda x, o: get_client() & o
|
|
__xor__ = lambda x, o: get_client() ^ o
|
|
__or__ = lambda x, o: get_client() | o
|
|
__div__ = lambda x, o: get_client().__div__(o)
|
|
__truediv__ = lambda x, o: get_client().__truediv__(o)
|
|
__neg__ = lambda x: -(get_client())
|
|
__pos__ = lambda x: +(get_client())
|
|
__abs__ = lambda x: abs(get_client())
|
|
__invert__ = lambda x: ~(get_client())
|
|
__complex__ = lambda x: complex(get_client())
|
|
__int__ = lambda x: int(get_client())
|
|
if not six.PY3:
|
|
__long__ = lambda x: long(get_client()) # NOQA
|
|
__float__ = lambda x: float(get_client())
|
|
__str__ = lambda x: six.binary_type(get_client())
|
|
__unicode__ = lambda x: six.text_type(get_client())
|
|
__oct__ = lambda x: oct(get_client())
|
|
__hex__ = lambda x: hex(get_client())
|
|
__index__ = lambda x: get_client().__index__()
|
|
__coerce__ = lambda x, o: x.__coerce__(x, o)
|
|
__enter__ = lambda x: x.__enter__()
|
|
__exit__ = lambda x, *a, **kw: x.__exit__(*a, **kw)
|
|
|
|
client = ProxyClient()
|
|
|
|
|
|
def get_option(x, d=None):
|
|
options = getattr(django_settings, 'RAVEN_CONFIG', {})
|
|
|
|
return getattr(django_settings, 'SENTRY_%s' % x, options.get(x, d))
|
|
|
|
|
|
def get_client(client=None):
|
|
global _client
|
|
|
|
tmp_client = client is not None
|
|
if not tmp_client:
|
|
client = getattr(django_settings, 'SENTRY_CLIENT', 'raven.contrib.django.DjangoClient')
|
|
|
|
if _client[0] != client:
|
|
module, class_name = client.rsplit('.', 1)
|
|
|
|
ga = lambda x, d=None: getattr(django_settings, 'SENTRY_%s' % x, d)
|
|
options = getattr(django_settings, 'RAVEN_CONFIG', {})
|
|
options.setdefault('servers', ga('SERVERS'))
|
|
options.setdefault('include_paths', ga('INCLUDE_PATHS', []))
|
|
options['include_paths'] = set(options['include_paths']) | get_installed_apps()
|
|
options.setdefault('exclude_paths', ga('EXCLUDE_PATHS'))
|
|
options.setdefault('timeout', ga('TIMEOUT'))
|
|
options.setdefault('name', ga('NAME'))
|
|
options.setdefault('auto_log_stacks', ga('AUTO_LOG_STACKS'))
|
|
options.setdefault('key', ga('KEY', md5(django_settings.SECRET_KEY.encode('utf8')).hexdigest()))
|
|
options.setdefault('string_max_length', ga('MAX_LENGTH_STRING'))
|
|
options.setdefault('list_max_length', ga('MAX_LENGTH_LIST'))
|
|
options.setdefault('site', ga('SITE'))
|
|
options.setdefault('public_key', ga('PUBLIC_KEY'))
|
|
options.setdefault('secret_key', ga('SECRET_KEY'))
|
|
options.setdefault('project', ga('PROJECT'))
|
|
options.setdefault('processors', ga('PROCESSORS'))
|
|
options.setdefault('dsn', ga('DSN'))
|
|
options.setdefault('context', ga('CONTEXT'))
|
|
|
|
class_name = str(class_name)
|
|
|
|
try:
|
|
Client = getattr(__import__(module, {}, {}, class_name), class_name)
|
|
except ImportError:
|
|
logger.exception('Failed to import client: %s', client)
|
|
if not _client[1]:
|
|
# If there is no previous client, set the default one.
|
|
client = 'raven.contrib.django.DjangoClient'
|
|
_client = (client, get_client(client))
|
|
else:
|
|
instance = Client(**options)
|
|
if not tmp_client:
|
|
_client = (client, instance)
|
|
return instance
|
|
return _client[1]
|
|
|
|
|
|
def sentry_exception_handler(request=None, **kwargs):
|
|
exc_type = sys.exc_info()[0]
|
|
|
|
exclusions = set(get_option('IGNORE_EXCEPTIONS', ()))
|
|
|
|
exc_name = '%s.%s' % (exc_type.__module__, exc_type.__name__)
|
|
if exc_type.__name__ in exclusions or exc_name in exclusions or any(exc_name.startswith(e[:-1]) for e in exclusions if e.endswith('*')):
|
|
logger.info(
|
|
'Not capturing exception due to filters: %s', exc_type,
|
|
exc_info=sys.exc_info())
|
|
return
|
|
|
|
try:
|
|
client.captureException(exc_info=sys.exc_info(), request=request)
|
|
except Exception as exc:
|
|
try:
|
|
logger.exception('Unable to process log entry: %s' % (exc,))
|
|
except Exception as exc:
|
|
warnings.warn('Unable to process log entry: %s' % (exc,))
|
|
|
|
|
|
def register_handlers():
|
|
from django.core.signals import got_request_exception
|
|
|
|
# HACK: support Sentry's internal communication
|
|
if 'sentry' in django_settings.INSTALLED_APPS:
|
|
from django.db import transaction
|
|
# Django 1.6
|
|
if hasattr(transaction, 'atomic'):
|
|
commit_on_success = transaction.atomic
|
|
else:
|
|
commit_on_success = transaction.commit_on_success
|
|
|
|
@commit_on_success
|
|
def wrap_sentry(request, **kwargs):
|
|
if transaction.is_dirty():
|
|
transaction.rollback()
|
|
return sentry_exception_handler(request, **kwargs)
|
|
|
|
exception_handler = wrap_sentry
|
|
else:
|
|
exception_handler = sentry_exception_handler
|
|
|
|
# Connect to Django's internal signal handler
|
|
got_request_exception.connect(exception_handler, weak=False)
|
|
|
|
# If Celery is installed, register a signal handler
|
|
if 'djcelery' in django_settings.INSTALLED_APPS:
|
|
try:
|
|
# Celery < 2.5? is not supported
|
|
from raven.contrib.celery import (
|
|
register_signal, register_logger_signal)
|
|
except ImportError:
|
|
logger.exception('Failed to install Celery error handler')
|
|
else:
|
|
try:
|
|
register_signal(client)
|
|
except Exception:
|
|
logger.exception('Failed to install Celery error handler')
|
|
|
|
try:
|
|
register_logger_signal(client)
|
|
except Exception:
|
|
logger.exception('Failed to install Celery error handler')
|
|
|
|
|
|
def register_serializers():
|
|
# force import so serializers can call register
|
|
import raven.contrib.django.serializers # NOQA
|
|
|
|
if ('raven.contrib.django' in django_settings.INSTALLED_APPS
|
|
or 'raven.contrib.django.raven_compat' in django_settings.INSTALLED_APPS):
|
|
register_handlers()
|
|
register_serializers()
|