I wanted to make my Django app send debugging information to slack instead of by email, which is the default.
ADMINS
settingNow, 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,
},
}
}
I would advise you to create a custom logging handler. You can have a look at the AdminEmailHandler implementation and make your own or, even simpler, if that suits your needs, subclass it and only override the send_mail
method.
import requests
from django.utils.log import AdminEmailHandler
class SlackHandler(AdminEmailHandler):
def send_mail(self, subject, message, *args, **kwargs):
html_message = kwargs.get('html_message')
requests.post("https://my-slack-url/", {...})
You then need to configure the LOGGING setting to use your new handler instead of AdminEmailHandler
. Here is Django's default logging configuration.
Example:
'handlers': {
'slack': {
'level': 'ERROR',
'filters': ['require_debug_false'],
'class': 'import.path.to.SlackHandler'
}
},
# ...
'loggers': {
'django.request': {
'handlers': ['slack'],
'level': 'ERROR',
'propagate': False,
},
# ...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With