Ticket #11565: error_email_rate_limit_v2.diff

File error_email_rate_limit_v2.diff, 3.6 KB (added by simon29, 5 years ago)

Falls back to memory if cache isn't available

  • core/handlers/base.py

     
     1try:
     2    from hashlib import md5
     3except ImportError:
     4    from md5 import md5
    15import sys
     6from datetime import datetime, timedelta
    27
     8from django.core.cache import cache                               
    39from django import http
    410from django.core import signals
    511from django.utils.encoding import force_unicode
     
    1319        http.fix_IE_for_attach,
    1420        http.fix_IE_for_vary,
    1521    ]
     22   
     23    _errors = {}
    1624
    1725    def __init__(self):
    1826        self._request_middleware = self._view_middleware = self._response_middleware = self._exception_middleware = None
     
    166174            return debug.technical_500_response(request, *exc_info)
    167175
    168176        # When DEBUG is False, send an error message to the admins.
    169         subject = 'Error (%s IP): %s' % ((request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS and 'internal' or 'EXTERNAL'), request.path)
    170         try:
    171             request_repr = repr(request)
    172         except:
    173             request_repr = "Request repr() unavailable"
    174         message = "%s\n\n%s" % (self._get_traceback(exc_info), request_repr)
    175         mail_admins(subject, message, fail_silently=True)
     177       
     178        # Where settings.ERROR_EMAIL_RATE_LIMIT > 0, limit to one duplicate
     179        # email per ERROR_EMAIL_RATE_LIMIT seconds. Use cache if available,
     180        # otherwise use local memory, limited to ERROR_EMAIL_KEY_LIMIT.
     181        # If using cache, prefix keys with ERROR_EMAIL_CACHE_PREFIX.
     182       
     183        # Note: none of the above settings are required!
     184       
     185        tb = self._get_traceback(exc_info)
     186       
     187        # Track duplicate errors
     188        duplicate = False
     189        rate = getattr(settings, 'ERROR_EMAIL_RATE_LIMIT', 0)  # seconds
     190        if rate > 0:
     191            key = md5(tb).hexdigest()
     192            prefix = getattr(settings, 'ERROR_EMAIL_CACHE_PREFIX', 'ERROR_EMAIL')
     193            # Test the cache works
     194            cache_key = '%s_%s' % (prefix, key)
     195            try:
     196                cache.set(prefix, 1, 1)
     197                use_cache = cache.get(prefix) == 1
     198            except:
     199                use_cache = False
     200            if use_cache:
     201                duplicate = cache.get(cache_key) == 1
     202                cache.set(cache_key, 1, rate)
     203            else:
     204                min_date = datetime.now() - timedelta(seconds=rate)
     205                max_keys = getattr(settings, 'ERROR_EMAIL_KEY_LIMIT', 100)
     206                duplicate = (key in self._errors and self._errors[key] >= min_date)
     207                self._errors = dict(filter(lambda x: x[1] >= min_date,
     208                                          sorted(self._errors.items(),
     209                                                 key=lambda x: x[1]))[0-max_keys:])
     210                if not duplicate:
     211                    self._errors[key] = datetime.now()
     212                   
     213        # Send the error
     214        if rate == 0 or not duplicate:
     215            subject = 'Error (%s IP): %s' % ((request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS and 'internal' or 'EXTERNAL'), request.path)
     216            try:
     217                request_repr = repr(request)
     218            except:
     219                request_repr = "Request repr() unavailable"
     220            message = "%s\n\n%s" % (tb, request_repr)
     221            mail_admins(subject, message, fail_silently=True)
     222
    176223        # If Http500 handler is not installed, re-raise last exception
    177224        if resolver.urlconf_module is None:
    178225            raise exc_info[1], None, exc_info[2]
Back to Top