在Django使用Logging製作紀錄檔

2018/06/08 posted in  Django comments

Logging是作為backend server必備的工具,透過log file,你可以知道程式執行了甚麼。如果你的server有unexpected error發生,最好的做法就是到server查看log file,找出發生甚麼事。

這篇就紀錄在Django設置logging的種種細節吧。

認識Python logging

為甚麼要用logging,不用print?

  • Custom log format
  • 不同程度的logging + custom handler
    • 例如把debugerror message log到不同的file
    • 用email匯報某個module的error
  • Custom filter
  • Per-module level settings
print('Something happened!')
# Something happened!

logger.debug('Something happened!')
# [INFO] 2018-06-08 22:00:01,850 | module_a.function_b:26 | Something happened!

用法

可以直接logging.debug(),但一般都會建立一個instance來使用:

# import the logging library
import logging

# Get an instance of a logger
logger = logging.getLogger(__name__)

logger.debug('')
logger.info('')
logger.warning('')
logger.error('')
logger.critical('')
logger.exception('')

__name__

用例子解釋:

Files

# b.py
def print_b_namespace():
    print(__name__)
# c/d.py
def print_d_namespace():
    print(__name__)

if __name__ == '__main__':
    print(__name__)
# a.py
from b import print_b_namespace
from c.d import print_d_namespace

print(__name__)
print_b_namespace()
print_d_namespace()

延伸閱讀:理解Python的 relative 和 absolute import

執行

$ python a.py
__main__
b
c.d

$ python c/d.py
__main__

結論

  • Entry point的__name____main__
  • 所有被entry point "import"的module都會一層一層地命名下去
    • 例如django中的models.py__name__就是some_app.models

好處

logger使用自己的__name__來分類,可以:

  1. 在log file中分辨出這些log來自哪裡
  2. 可以逐個module決定如何log、log到哪裡等等這些設定

logging module的更多用法

設置Django logging

Django支援python logging的設定。

Django logging

Sample config

# In settings.py
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'normal': {
            'format': '[%(levelname)s] %(asctime)s | %(name)s:%(lineno)d | %(message)s'
        },
        'simple': {
            'format': '[%(levelname)s] %(message)s'
        },
    },
    # 'filters': {
    #     'require_debug_true': {
    #         '()': 'django.utils.log.RequireDebugTrue',
    #     },
    # },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',  # Default logs to stderr
            'formatter': 'normal',  # use the above "normal" formatter
            # 'filters': ['require_debug_true'],  # add filters
        },
    },
    'loggers': {
        '': {  # means "root logger"
            'handlers': ['console'],  # use the above "console" handler
            'level': 'INFO',  # logging level
        },
        'some_app.some_module': {  # Modify logger in some modules
            'handlers': ['console'],
            'level': 'DEBUG',
            'propagate': True, 
        },
    },
}

詳細設定要同時查找Django docPython doc

Sentry

Sentry是一個error tracking工具,提供dashboard、email等功能,讓你可以即時查看server發生甚麼error。

Sentry支援各種語言和框架,官方也有django的整合教學,輕鬆設置後就可以隨時收到server的error消息,及早發現就能及早修復。

uWSGI

一般開發時,會使用manage.py runserver啟動server,這時logs就會output到console (for StreamHandler)。

但到實際production使用,多數會deploy到WSGI server,例如uWSGI,然後就不會再由console查看log訊息了。

延伸閱讀:入門理解WSGI
簡單教學:How To Serve Django Applications with uWSGI and Nginx on Ubuntu 16.04

此時Django有兩個做法:

  1. 在settings使用FileHandler等等,自行處理;或
  2. 在settings使用StreamHandler,把logs傳給uWSGI,然後uWSGI負責建立紀錄檔等等。

下面會講解如何使用第二種做法。

設定uWSGI

uWSGI的設定檔應該會在/etc/uwsgi/sites/your_project.ini,在設定檔加入:

# logging
logto = /var/log/uwsgi/this_is_a_log.log
log-maxsize = 1000000 # 每到達1MB,就會自動split一個新檔案出來

然後準備logging用的directory

sudo mkdir -p /var/log/uwsgi
sudo chown -R user:user /var/log/uwsgi  # 確保設定檔裡的user有權限
# 重啟uWSGI
sudo systemctl restart uwsgi

然後就可以查看/var/log/uwsgi/this_is_a_log.log了!

References

https://lincolnloop.com/blog/django-logging-right-way/