Files
zulip/zephyr/handlers.py
Tim Abbott b17741a5c4 [manual] Rename error bot to zulip.com domain.
Just before this is pushed to prod, we need to rename the Humbug error
bot in the database using:

./manage.py change_user_email humbug+errors@humbughq.com error-bot@zulip.com
/etc/init.d/memcached restart

No action is required on pushing this to staging, but in between when
this is pushed to staging and when it is pushed to prod (and that
transition performed), error reporting to humbug will not work on
staging.

(imported from commit 93044bb01797c981067f359676826d4a5791e235)
2013-07-25 17:16:54 -04:00

111 lines
4.3 KiB
Python

from __future__ import absolute_import
import logging
import traceback
import platform
from django.core import mail
from django.utils.log import AdminEmailHandler
from django.views.debug import ExceptionReporter, get_exception_reporter_filter
def format_record(record):
"""
Given a Django error LogRecord, format and return the interesting details,
for use by notification mechanisms like Humbug and e-mail.
"""
subject = '%s: %s' % (platform.node(), record.getMessage())
if record.exc_info:
stack_trace = ''.join(traceback.format_exception(*record.exc_info))
else:
stack_trace = 'No stack trace available'
try:
user_profile = record.request.user
user_info = "%s (%s)" % (user_profile.full_name, user_profile.email)
except Exception:
# Error was triggered by an anonymous user.
user_info = "Anonymous user (not logged in)"
return (subject, stack_trace, user_info)
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.lib.actions import internal_send_message
try:
request = record.request
filter = get_exception_reporter_filter(request)
request_repr = "Request info:\n~~~~\n"
request_repr += "- path: %s\n" % (request.path,)
if request.method == "GET":
request_repr += "- GET: %s\n" % (request.GET,)
elif request.method == "POST":
request_repr += "- POST: %s\n" % (filter.get_post_parameters(request),)
for field in ["REMOTE_ADDR", "QUERY_STRING", "SERVER_NAME"]:
request_repr += "- %s: \"%s\"\n" % (field, request.META.get(field, "(None)"))
request_repr += "~~~~"
except Exception:
request_repr = "Log record message:\n%s" % (record.getMessage(),)
subject, stack_trace, user_info = format_record(record)
try:
internal_send_message("error-bot@zulip.com",
"stream", "errors", self.format_subject(subject),
"Error generated by %s\n\n~~~~ pytb\n%s\n\n~~~~\n%s" % (
user_info, stack_trace, request_repr))
except:
# If this breaks, complain loudly but don't pass the traceback up the stream
# However, we *don't* want to use logging.exception since that could trigger a loop.
logging.warning("Reporting an exception triggered an exception!", exc_info=True)
def format_subject(self, subject):
"""
Escape CR and LF characters, and limit length to MAX_SUBJECT_LENGTH.
"""
from zephyr.models import MAX_SUBJECT_LENGTH
formatted_subject = subject.replace('\n', '\\n').replace('\r', '\\r')
return formatted_subject[:MAX_SUBJECT_LENGTH]
class HumbugAdminEmailHandler(AdminEmailHandler):
"""An exception log handler that emails log entries to site admins.
If the request is passed as the first argument to the log record,
request data will be provided in the email report.
"""
def emit(self, record):
try:
request = record.request
filter = get_exception_reporter_filter(request)
request_repr = filter.get_request_repr(request)
except Exception:
request = None
request_repr = "Log record message:\n%s" % (record.getMessage(),)
subject, stack_trace, user_info = format_record(record)
message = "Error generated by %s\n\n%s\n\n%s" % (user_info, stack_trace,
request_repr)
try:
reporter = ExceptionReporter(request, is_email=True, *record.exc_info)
html_message = self.include_html and reporter.get_traceback_html() or None
except Exception:
html_message = None
mail.mail_admins(self.format_subject(subject), message, fail_silently=True,
html_message=html_message)