将生产错误发送到 slack 而不是电子邮件
Send production errors to slack instead of email
我想让我的 Django 应用程序将调试信息发送到 slack 而不是默认的电子邮件。
- 禁用电子邮件很简单。只是不要在
ADMINS
设置 中添加任何电子邮件
- 向 slack 发送信息很容易。只需添加一个传入的 webhook
现在,我应该在哪里创建发送消息的逻辑?中间件似乎是个不错的主意。我会做类似
的事情
class ExceptionMiddleware:
def process_exception(self, request, exception):
pretty_debugging_message = ...
requests.post("https://my-slack-url/", {...})
中间件只是 return None
以免干扰系统的其余部分;毕竟,电子邮件已经被禁用了。
所以我的问题是:如何获得 django 收集的所有调试信息?我可以做类似
的事情
import sys, traceback
pretty_debugging_message = '\n'.join(
traceback.format_exception(*sys.exc_info())
)
但这只是提供回溯。所有本地人、会话、客户端 IP 等怎么样?
通读 https://docs.djangoproject.com/en/1.8/howto/error-reporting/,我得到的想法是,在处理中间件之前不会收集所有这些信息,也就是说,一旦尝试了一切,并且没有处理错误,然后 Django 运行它的 ErrorReporter 并记录信息。我可以以某种方式干预该过程并让它将信息发送到 slack 吗?那会更好吗?
更新
我的解决方案:
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
}
]
})
在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,
},
}
}
我对这个问题的最佳解决方案是
class ExceptionMiddleware:
def process_exception(self, request, exception):
reporter = ExceptionReporter(
request,
*sys.exc_info()
)
html = reporter.get_traceback_html()
plain = reporter..get_traceback_text()
requests.post("https://my-slack-url/", {...})
这似乎是django 检索所有数据的方式。它还允许获取 HTML 和纯文本版本,因此有一些关于如何显示它的选项。
我建议您创建一个自定义日志记录处理程序。您可以查看 AdminEmailHandler 实现并创建自己的实现,或者更简单,如果满足您的需要,将其子类化并仅重写 send_mail
方法。
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/", {...})
然后您需要配置 LOGGING setting to use your new handler instead of AdminEmailHandler
. Here is Django's default logging configuration。
示例:
'handlers': {
'slack': {
'level': 'ERROR',
'filters': ['require_debug_false'],
'class': 'import.path.to.SlackHandler'
}
},
# ...
'loggers': {
'django.request': {
'handlers': ['slack'],
'level': 'ERROR',
'propagate': False,
},
# ...
}
这仅适用于 500 个错误:
import requests
from django.views.debug import ExceptionReporter
SLACK_TEAM_NAME = "myteam"
class SlackHandler(AdminEmailHandler):
def emit(self, record):
### Copy the emit from AdminEmailHandler
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{}'.format(force_text(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)
### my stuff here
html_message = reporter.get_traceback_text()
token = "XXXXXXXXXXXXXXXX"
channel = "#XXXXXXXXXXXX"
html_channel = channel.replace("#","%23")
url = "https://%s.slack.com/services/hooks/slackbot?token=%s&channel=%s" % (SLACK_TEAM_NAME, token, html_channel)
#print url
r = requests.post(url, data=html_message)
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.settings.SlackHandler',
},
},
'loggers': {
'django.request': {
'handlers': ['slack'],
'level': 'ERROR',
'propagate': False,
},
},
}
我想让我的 Django 应用程序将调试信息发送到 slack 而不是默认的电子邮件。
- 禁用电子邮件很简单。只是不要在
ADMINS
设置 中添加任何电子邮件
- 向 slack 发送信息很容易。只需添加一个传入的 webhook
现在,我应该在哪里创建发送消息的逻辑?中间件似乎是个不错的主意。我会做类似
的事情class ExceptionMiddleware:
def process_exception(self, request, exception):
pretty_debugging_message = ...
requests.post("https://my-slack-url/", {...})
中间件只是 return None
以免干扰系统的其余部分;毕竟,电子邮件已经被禁用了。
所以我的问题是:如何获得 django 收集的所有调试信息?我可以做类似
的事情import sys, traceback
pretty_debugging_message = '\n'.join(
traceback.format_exception(*sys.exc_info())
)
但这只是提供回溯。所有本地人、会话、客户端 IP 等怎么样?
通读 https://docs.djangoproject.com/en/1.8/howto/error-reporting/,我得到的想法是,在处理中间件之前不会收集所有这些信息,也就是说,一旦尝试了一切,并且没有处理错误,然后 Django 运行它的 ErrorReporter 并记录信息。我可以以某种方式干预该过程并让它将信息发送到 slack 吗?那会更好吗?
更新
我的解决方案:
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
}
]
})
在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,
},
}
}
我对这个问题的最佳解决方案是
class ExceptionMiddleware:
def process_exception(self, request, exception):
reporter = ExceptionReporter(
request,
*sys.exc_info()
)
html = reporter.get_traceback_html()
plain = reporter..get_traceback_text()
requests.post("https://my-slack-url/", {...})
这似乎是django 检索所有数据的方式。它还允许获取 HTML 和纯文本版本,因此有一些关于如何显示它的选项。
我建议您创建一个自定义日志记录处理程序。您可以查看 AdminEmailHandler 实现并创建自己的实现,或者更简单,如果满足您的需要,将其子类化并仅重写 send_mail
方法。
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/", {...})
然后您需要配置 LOGGING setting to use your new handler instead of AdminEmailHandler
. Here is Django's default logging configuration。
示例:
'handlers': {
'slack': {
'level': 'ERROR',
'filters': ['require_debug_false'],
'class': 'import.path.to.SlackHandler'
}
},
# ...
'loggers': {
'django.request': {
'handlers': ['slack'],
'level': 'ERROR',
'propagate': False,
},
# ...
}
这仅适用于 500 个错误:
import requests
from django.views.debug import ExceptionReporter
SLACK_TEAM_NAME = "myteam"
class SlackHandler(AdminEmailHandler):
def emit(self, record):
### Copy the emit from AdminEmailHandler
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{}'.format(force_text(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)
### my stuff here
html_message = reporter.get_traceback_text()
token = "XXXXXXXXXXXXXXXX"
channel = "#XXXXXXXXXXXX"
html_channel = channel.replace("#","%23")
url = "https://%s.slack.com/services/hooks/slackbot?token=%s&channel=%s" % (SLACK_TEAM_NAME, token, html_channel)
#print url
r = requests.post(url, data=html_message)
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.settings.SlackHandler',
},
},
'loggers': {
'django.request': {
'handlers': ['slack'],
'level': 'ERROR',
'propagate': False,
},
},
}