title: Send production errors to slack instead of email
body: <p>I wanted to make my Django app send debugging information to slack instead of by email, which is the default.</p>
  
  <ul>
  <li>Disabling email is simple. Just don't put any emails in <code>ADMINS</code> setting</li>
  <li>Sending information to slack is easy. Just add an incoming webhook</li>
  </ul>
  
  <p>Now, where should i create the logic that sends the messge? Middleware seems to be a pretty good idea. I would do something like</p>
  
  <pre><code>class ExceptionMiddleware:
      def process_exception(self, request, exception):
          pretty_debugging_message = ...
          requests.post("https://my-slack-url/", {...})
  </code></pre>
  
  <p>The middleware would just return <code>None</code> so as to not interfere with the rest of the system; after all, the emailing has already been disabled.</p>
  
  <p>So my question is: How do I get all the debugging info goodness that django collects? I can do something like</p>
  
  <pre><code>import sys, traceback
  pretty_debugging_message = '\n'.join(
      traceback.format_exception(*sys.exc_info())
  )
  </code></pre>
  
  <p>But this only provides the traceback. What about all of the locals, the session, the clients IP etc.?</p>
  
  <p>Reading through <a href="https://docs.djangoproject.com/en/1.8/howto/error-reporting/" rel="nofollow">https://docs.djangoproject.com/en/1.8/howto/error-reporting/</a>, 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?</p>
  
  <p><strong>Update</strong></p>
  
  <p>My solution:</p>
  
  <pre><code>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
                  }
              ]
          })
  </code></pre>
  
  <p>In settings.py:</p>
  
  <pre><code>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,
          },
      }
  }
  </code></pre>