發送Django error log 到企業微信,python+微信實現即時告警
Django的logging系統十分好用,使用file,mail_admins之類的handlers可以快捷地實現保存日誌到文件,發送錯誤日誌給管理員的功能。但是,如果能直接將應用的錯誤日誌發送到手機上,實現即時告警,豈不是更好?
註冊企業微信1
首先需要註冊一個企業微信賬號,地址是:
https://work.weixin.qq.com/wework_admin/register_wx?from=myhome注意:
註冊過程如果是爲企業使用要選擇企業,然後上傳企業的資質證明,如果是個人註冊,選擇團隊,然後輸入自己的身份證號即可完成註冊.
然後進入企業應用頁面,添加一個應用,添加完成後,進入應用頁面:
- 這裏的agentid和secret需要留意, 後續的發送信息api需要它們。
- 另外我們還需要corpid,在我的企業-企業信息中可以找到。
使用企業微信API發送消息
測試使用企業微信API發送消息:
企業微信API:發送消息
例子:#! /usr/bin/env python # -*- coding: utf-8 -*- import requests import json def get_token(): url = 'https://qyapi.weixin.qq.com/cgi-bin/gettoken' values = { 'corpid': '<YOUR CORPID>', 'corpsecret': '<YOUR SECRET>', } req = requests.post(url, params=values) data = json.loads(req.text) return data["access_token"] def send_msg(): url = ("https://qyapi.weixin.qq.com/cgi-bin/message/send" "?access_token={}").format(get_token()) values = { # "touser": "@all", "toparty": "2", "msgtype": "text", "agentid": "<YOUR AGENTID>", "text": { "content": u"報警測試,toparty: 2" }, } req = requests.post(url, json.dumps(values)) print(values, req) if __name__ == '__main__': send_msg()
其中toparty:2,意爲向id爲2的部門的所有成員發送消息。
使用touser:@all, 可以向所有人發送信息。
發送django的log到企業微信
配置Django的logging系統
接下來我們需要配置一下django的logging系統,來發送ERROR級別的log到企業微信。
我的思路是可以參照django自帶的AdminEmailHandler寫一個WechatAlarmHandler。
代碼如下:import logging import requests import json from copy import copy from django.core.cache import cache from django.views.debug import ExceptionReporter class WechatAlarmHandler(logging.Handler): """An exception log handler that sends log entries to wechat alarm bot. If the request is passed as the first argument to the log record, request data will be provided in the email report. """ def __init__(self): 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 # NOQA 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(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()) self.send_msg(subject, message) def send_msg(self, subject, message, *args, **kwargs): WechatAlarm().send_msg('{}'.format(subject)) def format_subject(self, subject): """ Escape CR and LF characters. """ return subject.replace('\n', '\\n').replace('\r', '\\r') class WechatAlarm: def __init__(self, corpid='<YOUR CORPID>', corpsecret='<YOUR SECRET>', agentid='<YOUR AGENTID>', partyid='<YOUR PARTYID>'): self.corpid = corpid self.partyid = partyid self.key = 'wechat_send_alarm_key' self.corpsecret = corpsecret self.agentid = agentid def get_token(self): token = cache.get(self.key) if token: return token else: url = 'https://qyapi.weixin.qq.com/cgi-bin/gettoken' values = { 'corpid': self.corpid, 'corpsecret': self.corpsecret, } req = requests.post(url, params=values) data = json.loads(req.text) cache.set(self.key, data["access_token"], 7200) return data["access_token"] def send_msg(self, content=None): url = ("https://qyapi.weixin.qq.com/cgi-bin/message/send" "?access_token={}").format(self.get_token()) values = { # "touser": "@all", "toparty": self.partyid, "msgtype": "text", "agentid": self.agentid, "text": { "content": content, }, } return requests.post(url, json.dumps(values))
再配置一下django的settings裏的LOGGING:
GGING = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'verbose': { 'format': '[%(asctime)s](%(levelname)s)<%(name)s.%(funcName)s>{%(process)d/%(thread)d} : %(message)s' }, 'simple': { 'format': '%(levelname)s %(message)s' }, }, 'filters': { 'require_debug_false': { '()': 'django.utils.log.RequireDebugFalse' } }, 'handlers': { 'null': { 'level': 'DEBUG', 'class': 'django.utils.log.NullHandler', }, 'console': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', 'formatter': 'simple' }, 'mail_admins': { 'level': 'ERROR', 'class': 'django.utils.log.AdminEmailHandler', 'filters': ['require_debug_false'], }, 'send_wechat': { 'level': 'ERROR', 'class': 'utils.log.WechatAlarmHandler', # your handler path }, 'file': { 'level': 'INFO', 'class': 'logging.handlers.TimedRotatingFileHandler', 'formatter': 'verbose', 'filename': webservice_logfile, 'when': 'D' }, }, 'loggers': { '': { 'handlers': ['file', 'mail_admins', 'send_wechat'], 'propagate': True, 'level': 'ERROR', }, 'django': { 'handlers': ['file', 'mail_admins'], 'propagate': True, 'level': 'ERROR', }, 'django.request': { 'handlers': ['file', 'mail_admins', ], 'level': 'ERROR', 'propagate': True, }, } }
即,在handler裏增加了一個send_wechat, 在loggers裏的handers裏增加了send_wechat。
這樣django的error log就會通過企業微信發送到手機了。