Files
zulip/zerver/handlers.py
Kevin Mehall f7f2ec0aca [puppet] Report enterprise and prod errors to staging.
Errors are sent to a queue processor that posts them to staging,
just like the feedback bot.

(imported from commit 4a8d099672a1b3e48a8bc94148d8b53db73d2c64)
2013-11-13 16:22:21 -05:00

130 lines
4.7 KiB
Python

from __future__ import absolute_import
from django.conf import settings
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
from zerver.lib.queue import queue_json_publish
def format_record(record):
"""
Given a Django error LogRecord, format and return the interesting details,
for use by notification mechanisms like Zulip 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 AdminZulipHandler(logging.Handler):
"""An exception log handler that sends the exception to the queue to be
sent to the Zulip feedback server.
"""
# adapted in part from django/utils/log.py
def __init__(self):
logging.Handler.__init__(self)
def emit(self, record):
try:
request = record.request
filter = get_exception_reporter_filter(request)
if record.exc_info:
stack_trace = ''.join(traceback.format_exception(*record.exc_info))
else:
stack_trace = None
try:
user_profile = request.user
user_full_name = user_profile.full_name
user_email = user_profile.email
except Exception:
traceback.print_exc()
# Error was triggered by an anonymous user.
user_full_name = None
user_email = None
report = dict(
node = platform.node(),
method = request.method,
path = request.path,
data = request.GET if request.method == 'GET'
else filter.get_post_parameters(request),
remote_addr = request.META.get('REMOTE_ADDR', None),
query_string = request.META.get('QUERY_STRING', None),
server_name = request.META.get('SERVER_NAME', None),
message = record.getMessage(),
stack_trace = stack_trace,
user_full_name = user_full_name,
user_email = user_email,
)
except:
traceback.print_exc()
report = dict(
node = platform.node(),
message = record.getMessage(),
)
try:
if not settings.STAGING_DEPLOYED:
queue_json_publish('error_reports', dict(
type = "server",
report = report,
), lambda x: None)
else:
# On staging, process the report directly so it can happen inside this
# try/except to prevent looping
from zilencer.error_notify import notify_server_error
notify_server_error(report)
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)
class ZulipAdminEmailHandler(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)