---
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/)