mirror of https://github.com/zulip/zulip.git
Log errors to Humbug, too.
(imported from commit 2547625135568f3ea004bf4287471a82bc0a4f38)
This commit is contained in:
parent
adf289c9df
commit
a604183c5b
|
@ -4,8 +4,7 @@ from datetime import datetime, timedelta
|
|||
|
||||
# Adapted http://djangosnippets.org/snippets/2242/ by user s29 (October 25, 2010)
|
||||
|
||||
class RateLimitFilter(object):
|
||||
|
||||
class _RateLimitFilter(object):
|
||||
last_error = 0
|
||||
|
||||
def filter(self, record):
|
||||
|
@ -14,7 +13,8 @@ class RateLimitFilter(object):
|
|||
|
||||
# Track duplicate errors
|
||||
duplicate = False
|
||||
rate = getattr(settings, 'ERROR_RATE_LIMIT', 600) # seconds
|
||||
rate = getattr(settings, '%s_LIMIT' % self.__class__.__name__.upper(),
|
||||
600) # seconds
|
||||
if rate > 0:
|
||||
# Test if the cache works
|
||||
try:
|
||||
|
@ -24,8 +24,9 @@ class RateLimitFilter(object):
|
|||
use_cache = False
|
||||
|
||||
if use_cache:
|
||||
duplicate = cache.get('ERROR_RATE') == 1
|
||||
cache.set('ERROR_RATE', 1, rate)
|
||||
key = self.__class__.__name__.upper()
|
||||
duplicate = cache.get(key) == 1
|
||||
cache.set(key, 1, rate)
|
||||
else:
|
||||
min_date = datetime.now() - timedelta(seconds=rate)
|
||||
duplicate = (self.last_error >= min_date)
|
||||
|
@ -33,3 +34,9 @@ class RateLimitFilter(object):
|
|||
self.last_error = datetime.now()
|
||||
|
||||
return not duplicate
|
||||
|
||||
class HumbugLimiter(_RateLimitFilter):
|
||||
pass
|
||||
|
||||
class EmailLimiter(_RateLimitFilter):
|
||||
pass
|
||||
|
|
|
@ -154,7 +154,6 @@ if deployed:
|
|||
'LOCATION': '127.0.0.1:11211',
|
||||
'TIMEOUT': 3600
|
||||
} }
|
||||
error_filters = ['ratelimit']
|
||||
else:
|
||||
CACHES = { 'default': {
|
||||
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
||||
|
@ -164,9 +163,6 @@ else:
|
|||
'MAX_ENTRIES': 100000
|
||||
}
|
||||
} }
|
||||
error_filters = []
|
||||
|
||||
ERROR_RATE_LIMIT=600
|
||||
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
|
@ -177,11 +173,23 @@ LOGGING = {
|
|||
}
|
||||
},
|
||||
'filters': {
|
||||
'ratelimit': {
|
||||
'()': 'humbug.ratelimit.RateLimitFilter',
|
||||
'HumbugLimiter': {
|
||||
'()': 'humbug.ratelimit.HumbugLimiter',
|
||||
},
|
||||
'EmailLimiter': {
|
||||
'()': 'humbug.ratelimit.EmailLimiter',
|
||||
},
|
||||
'require_debug_false': {
|
||||
'()': 'django.utils.log.RequireDebugFalse',
|
||||
}
|
||||
},
|
||||
'handlers': {
|
||||
'inapp': {
|
||||
'level': 'ERROR',
|
||||
'class': 'zephyr.handlers.AdminHumbugHandler',
|
||||
'filters': ['HumbugLimiter', 'require_debug_false'],
|
||||
'formatter': 'default'
|
||||
},
|
||||
'console': {
|
||||
'level': 'DEBUG',
|
||||
'class': 'logging.StreamHandler',
|
||||
|
@ -196,12 +204,12 @@ LOGGING = {
|
|||
'mail_admins': {
|
||||
'level': 'ERROR',
|
||||
'class': 'django.utils.log.AdminEmailHandler',
|
||||
'filters': error_filters,
|
||||
'filters': ['EmailLimiter', 'require_debug_false'],
|
||||
},
|
||||
},
|
||||
'loggers': {
|
||||
'': {
|
||||
'handlers': ['console', 'file', 'mail_admins'],
|
||||
'handlers': ['inapp', 'console', 'file', 'mail_admins'],
|
||||
'level': 'INFO'
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
import sys
|
||||
import logging
|
||||
import traceback
|
||||
|
||||
from django.utils.timezone import now
|
||||
from django.views.debug import get_exception_reporter_filter
|
||||
|
||||
|
||||
class AdminHumbugHandler(logging.Handler):
|
||||
"""An exception log handler that Humbugs log entries to the Humbug realm.
|
||||
|
||||
If the request is passed as the first argument to the log record,
|
||||
request data will be provided in the email report.
|
||||
"""
|
||||
|
||||
# adapted in part from django/utils/log.py
|
||||
|
||||
def __init__(self):
|
||||
logging.Handler.__init__(self)
|
||||
|
||||
def emit(self, record):
|
||||
# We have to defer imports to avoid circular imports in settings.py.
|
||||
from zephyr.models import Message, UserProfile, Recipient, \
|
||||
create_stream_if_needed, get_client, do_send_message
|
||||
from django.conf import settings
|
||||
message = Message()
|
||||
message.sender = UserProfile.objects.get(user__email="humbug+errors@humbughq.com")
|
||||
message.recipient = Recipient.objects.get(type_id=create_stream_if_needed(
|
||||
message.sender.realm, "devel").id, type=Recipient.STREAM)
|
||||
message.pub_date = now()
|
||||
message.sending_client = get_client("Internal")
|
||||
|
||||
try:
|
||||
request = record.request
|
||||
subject = '%s (%s IP): %s' % (
|
||||
record.levelname,
|
||||
(request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS
|
||||
and 'internal' or 'EXTERNAL'),
|
||||
record.getMessage()
|
||||
)
|
||||
filter = get_exception_reporter_filter(request)
|
||||
request_repr = filter.get_request_repr(request)
|
||||
except Exception:
|
||||
subject = '%s: %s' % (
|
||||
record.levelname,
|
||||
record.getMessage()
|
||||
)
|
||||
request = None
|
||||
request_repr = "Request repr() unavailable."
|
||||
message.subject = self.format_subject(subject)
|
||||
|
||||
if record.exc_info:
|
||||
stack_trace = '\n'.join(traceback.format_exception(*record.exc_info))
|
||||
else:
|
||||
stack_trace = 'No stack trace available'
|
||||
|
||||
message.content = "~~~~ pytb\n%s\n\n%s\n~~~~" % (stack_trace, request_repr)
|
||||
do_send_message(message)
|
||||
|
||||
def format_subject(self, subject):
|
||||
"""
|
||||
Escape CR and LF characters, and limit length to 60 characters.
|
||||
"""
|
||||
formatted_subject = subject.replace('\n', '\\n').replace('\r', '\\r')
|
||||
return formatted_subject[:60]
|
||||
|
Loading…
Reference in New Issue