Skip to content

Commit

Permalink
Merge pull request #15 from garnertb/exception-handler
Browse files Browse the repository at this point in the history
Add a SlackExceptionHandler.
  • Loading branch information
lamby committed Oct 15, 2015
2 parents c403e69 + bf820a3 commit 984912e
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 0 deletions.
22 changes: 22 additions & 0 deletions django_slack/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,28 @@
please visit the `Slack API Attachments Docs
<https://api.slack.com/docs/attachments>`_.
Logging Exceptions to Slack
---------------------------
You can send logging messages from Django to Slack using the SlackExceptionHandler:
#. Add a handler that uses the SlackExceptionHandler class to the LOGGING
dictionary in your settings file::
LOGGING = {
'handlers': {
'slack_admins': {
'level': 'ERROR',
'filters': ['require_debug_false'],
'class': 'django_slack.logs.SlackExceptionHandler'
}
},
"loggers": {
"django": {
"handlers": ["console", "slack_admins"], "level": "ERROR", },
}
}
Configuration
-------------
Expand Down
85 changes: 85 additions & 0 deletions django_slack/logs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import copy
import logging
from . import slack_message
from django.conf import settings
from django.views.debug import ExceptionReporter


class SlackExceptionHandler(logging.Handler):
"""
An exception log handler that sends log entries to a Slack channel.
"""

def __init__(self, **kwargs):
# pop any kwargs that shouldn't be passed into the Slack message attachment here.
self.template = kwargs.pop('template', 'django_slack/exception')
self.kwargs = kwargs
logging.Handler.__init__(self)

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(),
)
except Exception:
subject = '%s: %s' % (
record.levelname,
record.getMessage(),
)
request = None
subject = self.format_subject(subject)

# Since we add a nicely formatted traceback on our own, create a copy
# of the log record without the exception data.
no_exc_record = copy.copy(record)
no_exc_record.exc_info = None
no_exc_record.exc_text = None

if record.exc_info:
exc_info = record.exc_info
else:
exc_info = (None, record.getMessage(), None)

reporter = ExceptionReporter(request, is_email=True, *exc_info)
message = "%s\n\n%s" % (self.format(no_exc_record), reporter.get_traceback_text())

colors = {
'ERROR': 'danger',
'WARNING': 'warning',
'INFO': 'good',
}

attachments = {
'title': subject,
'text': message,
'color': colors.get(record.levelname, '#AAAAAA'),
}

attachments.update(self.kwargs)
self.send_message(self.template, {'text': subject},
self.generate_attachments(**attachments))

def generate_attachments(self, **kwargs):
"""
Hook to override attachments.
"""
return [kwargs]

def send_message(self, *args, **kwargs):
"""
Hook to update the message before sending.
"""
return slack_message(*args, **kwargs)

def format_subject(self, subject):
"""
Escape CR and LF characters, and limit length.
RFC 2822's hard limit is 998 characters per line. So, minus "Subject: "
the actual subject must be no longer than 989 characters.
"""
formatted_subject = subject.replace('\n', '\\n').replace('\r', '\\r')
return formatted_subject[:989]
10 changes: 10 additions & 0 deletions django_slack/templates/django_slack/exception
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{% extends django_slack %}

{% comment %}
This is the default template for the SlackExceptionHandler, which
is intended to work out of the box without requiring user modification.
{% endcomment %}

{% block text %}
{{ text }}
{% endblock %}

0 comments on commit 984912e

Please sign in to comment.