I wanted to make my Django app send debugging information to slack instead of by email, which is the default.
- Disabling email is simple. Just don't put any emails in
ADMINS
setting - Sending information to slack is easy. Just add an incoming webhook
Now, where should i create the logic that sends the messge? Middleware seems to be a pretty good idea. I would do something like
class ExceptionMiddleware:
def process_exception(self, request, exception):
pretty_debugging_message = ...
requests.post("https://my-slack-url/", {...})
The middleware would just return None
so as to not interfere with the rest of the system; after all, the emailing has already been disabled.
So my question is: How do I get all the debugging info goodness that django collects? I can do something like
import sys, traceback
pretty_debugging_message = '\n'.join(
traceback.format_exception(*sys.exc_info())
)
But this only provides the traceback. What about all of the locals, the session, the clients IP etc.?
Reading through https://docs.djangoproject.com/en/1.8/howto/error-reporting/, I get the idea that all of that information is not collected until after middleware is handled, that is, once everything is tried, and an error has not been handled, then Django runs its ErrorReporter and logs the information. Could I intervene with that process somehow and make it send the info to slack? Would that be better?
Update
My solution:
class SlackHandler(AdminEmailHandler):
def emit(self, record):
try:
request = record.request
subject = '%s (%s IP): %s' % (
record.levelname,
('internal' if request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS
else 'EXTERNAL'),
record.getMessage()
)
filter = get_exception_reporter_filter(request)
request_repr = '\n{0}'.format(filter.get_request_repr(request))
except Exception:
subject = '%s: %s' % (
record.levelname,
record.getMessage()
)
request = None
request_repr = "unavailable"
subject = self.format_subject(subject)
if record.exc_info:
exc_info = record.exc_info
else:
exc_info = (None, record.getMessage(), None)
message = "%s\n\nRequest repr(): %s" % (self.format(record), request_repr)
reporter = ExceptionReporter(request, is_email=True, *exc_info)
html_message = reporter.get_traceback_html() if self.include_html else None
requests.post(settings.SLACK_WEBHOOK_URL, json={
"fallback": message,
"pretext": "An error occured",
"color": "#ef2a2a",
"fields": [
{
"title": "Error",
"value": message,
"short": False
}
]
})
In settings.py:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'filters': {
'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse'
}
},
'handlers': {
'slack': {
'level': 'ERROR',
'filters': ['require_debug_false'],
'class': 'myapp.myapp.SlackHandler'
}
# 'mail_admins': {
# 'level': 'ERROR',
# 'filters': ['require_debug_false'],
# 'class': 'django.utils.log.AdminEmailHandler'
# }
},
'loggers': {
'django.request': {
'handlers': ['slack'],
'level': 'ERROR',
'propagate': True,
},
}
}