--- tags: Django --- # [Django] Custom log handler 我想要在系統出錯的時候,自動發信給管理員/IT人員。 ## 需求 Django提供`AdminEmailHandler`以處理Django Application發生異常時,自動寄信通知管理員。 這裡所謂的管理員是只settings裡面`ADMIN`參數設定的資料,也代表未來要更改管理員就要動到code,當然如果有統一收信窗口就比較沒有影響。 但我的情況不是這樣,我習慣用Admin site當作管理資料的GUI,superuser的角色只會給IT人員使用,所以就打算寫一個能夠從`UserModel`取得`superuser`當作收件人的log handler. ## 研究 **先來瞧瞧AdminEmailHandler的寫法:** ```python=false class AdminEmailHandler(logging.Handler): ... def emit(self, record): ... html_message = reporter.get_traceback_html() if self.include_html else None self.send_mail(subject, message, fail_silently=True, html_message=html_message) def send_mail(self, subject, message, *args, **kwargs): mail.mail_admins(subject, message, *args, connection=self.connection(), **kwargs) ... ``` 可以發現發信的method使用內建的`mail_admins`,往下追或是官方文件也寫了,這個method裡面確實讀取的收件人email來自settings的`ADMIN`參數設定。 所以呢,可以繼承這個已經寫好的handler,再另外寫一個method覆蓋`mail_admins`就好。 ## 實作 首先要有一個替代mail_admins的method。 ```python=false def mail_superusers(subject='', body='', fail_silently=False, html_message=''): """Send a message to the superusers whom is set to be a superuser.""" # get superusers user_model = apps.get_model('base', 'User') superusers = user_model.objects.filter(is_superuser=True) # get their emails email_addrs = [user.email for user in superusers] connection = get_connection() mail = EmailMultiAlternatives( subject=subject, body=body, to=email_addrs, connection=connection ) if html_message: mail.attach_alternative(html_message, 'text/html') mail.send(fail_silently=fail_silently) ``` 這裡踩了一個小坑,User必須是**lazy import**,否則Application無法順利執行。 再來是custom log handler,非常簡單,就只需要把send_mail覆寫就行: ```python=false class SuperUserEmailHandler(AdminEmailHandler): def send_mail(self, subject, message, *args, **kwargs): mail_superusers(subject, message, *args, **kwargs) ``` 最後放到LOGGING設定裡面: ```python=false 'mail_mis': { 'level': 'ERROR', 'filters': ['require_debug_false'], 'class': 'log_handler.SuperUserEmailHandler', 'include_html': True, } ``` 結束! 但是他預設會傳送一整份degug頁面(就是DEBUG=True會看見的黃頁) ,我也沒有特別去改寫要傳送的內容,所以email內會包含很詳細的bug追蹤,是有點危險的喔。 ## 寫在幾年之後 前面是一個人負責所有系統開發與維護,還不容易請到經費租用服務的情況下,土炮的做法。 其實要接 Sentry 這種程式錯誤監控服務,就不用一天到晚收錯誤信了~ ## References [Django docs - AdminEmailHandler](https://docs.djangoproject.com/en/2.2/topics/logging/#django.utils.log.AdminEmailHandler) [Stackoverflow - How to Lazy Load a model in a managers to stop circular imports?](https://stackoverflow.com/questions/29744016/how-to-lazy-load-a-model-in-a-managers-to-stop-circular-imports) [寫得更詳盡 - Django 打造 logging handler 將 exception 送到 Slack](https://myapollo.com.tw/zh-tw/django-customize-logging-handler/)