2013-10-10 21:37:26 +02:00
|
|
|
from __future__ import absolute_import
|
|
|
|
|
2017-02-26 04:58:25 +01:00
|
|
|
from django.utils import timezone
|
|
|
|
|
2016-08-27 07:55:54 +02:00
|
|
|
import hashlib
|
2013-03-15 17:03:56 +01:00
|
|
|
import logging
|
2016-08-27 07:55:54 +02:00
|
|
|
import traceback
|
2012-12-05 18:01:43 +01:00
|
|
|
from datetime import datetime, timedelta
|
2016-09-10 00:36:17 +02:00
|
|
|
from zerver.lib.str_utils import force_bytes
|
2012-12-05 18:01:43 +01:00
|
|
|
|
|
|
|
# Adapted http://djangosnippets.org/snippets/2242/ by user s29 (October 25, 2010)
|
|
|
|
|
2012-12-06 22:00:34 +01:00
|
|
|
class _RateLimitFilter(object):
|
2012-12-11 02:02:58 +01:00
|
|
|
last_error = datetime.min
|
2012-12-05 18:01:43 +01:00
|
|
|
|
|
|
|
def filter(self, record):
|
2016-06-05 04:20:00 +02:00
|
|
|
# type: (logging.LogRecord) -> bool
|
2012-12-05 18:01:43 +01:00
|
|
|
from django.conf import settings
|
|
|
|
from django.core.cache import cache
|
|
|
|
|
|
|
|
# Track duplicate errors
|
|
|
|
duplicate = False
|
2016-11-30 22:49:02 +01:00
|
|
|
rate = getattr(settings, '%s_LIMIT' % self.__class__.__name__.upper(),
|
2016-12-03 00:04:17 +01:00
|
|
|
600) # seconds
|
2012-12-05 18:01:43 +01:00
|
|
|
if rate > 0:
|
|
|
|
# Test if the cache works
|
|
|
|
try:
|
|
|
|
cache.set('RLF_TEST_KEY', 1, 1)
|
|
|
|
use_cache = cache.get('RLF_TEST_KEY') == 1
|
2017-03-05 10:25:27 +01:00
|
|
|
except Exception:
|
2012-12-05 18:01:43 +01:00
|
|
|
use_cache = False
|
|
|
|
|
|
|
|
if use_cache:
|
2016-12-20 20:42:57 +01:00
|
|
|
if record.exc_info is not None:
|
|
|
|
tb = force_bytes('\n'.join(traceback.format_exception(*record.exc_info)))
|
|
|
|
else:
|
|
|
|
tb = force_bytes(str(record))
|
2016-08-27 07:55:54 +02:00
|
|
|
key = self.__class__.__name__.upper() + hashlib.sha1(tb).hexdigest()
|
2012-12-06 22:00:34 +01:00
|
|
|
duplicate = cache.get(key) == 1
|
2016-08-27 07:55:54 +02:00
|
|
|
if not duplicate:
|
|
|
|
cache.set(key, 1, rate)
|
2012-12-05 18:01:43 +01:00
|
|
|
else:
|
2017-02-26 04:58:25 +01:00
|
|
|
min_date = timezone.now() - timedelta(seconds=rate)
|
2012-12-05 18:01:43 +01:00
|
|
|
duplicate = (self.last_error >= min_date)
|
|
|
|
if not duplicate:
|
2017-02-26 04:58:25 +01:00
|
|
|
self.last_error = timezone.now()
|
2012-12-05 18:01:43 +01:00
|
|
|
|
|
|
|
return not duplicate
|
2012-12-06 22:00:34 +01:00
|
|
|
|
2013-08-06 21:37:34 +02:00
|
|
|
class ZulipLimiter(_RateLimitFilter):
|
2012-12-06 22:00:34 +01:00
|
|
|
pass
|
|
|
|
|
|
|
|
class EmailLimiter(_RateLimitFilter):
|
|
|
|
pass
|
2013-03-15 17:03:56 +01:00
|
|
|
|
|
|
|
class ReturnTrue(logging.Filter):
|
|
|
|
def filter(self, record):
|
2016-06-05 04:20:00 +02:00
|
|
|
# type: (logging.LogRecord) -> bool
|
2013-03-15 17:03:56 +01:00
|
|
|
return True
|
2013-06-10 18:57:59 +02:00
|
|
|
|
|
|
|
class RequireReallyDeployed(logging.Filter):
|
|
|
|
def filter(self, record):
|
2016-06-05 04:20:00 +02:00
|
|
|
# type: (logging.LogRecord) -> bool
|
2013-06-10 18:57:59 +02:00
|
|
|
from django.conf import settings
|
2015-08-21 09:18:44 +02:00
|
|
|
return settings.PRODUCTION
|
2017-01-18 12:52:01 +01:00
|
|
|
|
|
|
|
def skip_200_and_304(record):
|
|
|
|
# type: (logging.LogRecord) -> bool
|
|
|
|
# Apparently, `status_code` is added by Django and is not an actual
|
|
|
|
# attribute of LogRecord; as a result, mypy throws an error if we
|
|
|
|
# access the `status_code` attribute directly.
|
|
|
|
if getattr(record, 'status_code') in [200, 304]:
|
|
|
|
return False
|
|
|
|
|
|
|
|
return True
|