1. 程式人生 > >Python 中 logging 日誌模組在多程序環境下的使用

Python 中 logging 日誌模組在多程序環境下的使用

使用 Python 來寫後臺任務時,時常需要使用輸出日誌來記錄程式執行的狀態,並在發生錯誤時將錯誤的詳細資訊儲存下來,以別調試和分析。Python 的 logging 模組就是這種情況下的好幫手。

logging 模組可以指定日誌的級別,DEBUG、INFO、WARNING、ERROR、CRITICAL,例如可以在開發和除錯時,把 DEBUG 以上級別的日誌都輸出,而在生產環境下,只輸出 INFO 級別。(如果不特別指定,預設級別是 warning)

logging 還可以指定輸出到命令列或者檔案,還可以按時間或大小分割日誌檔案。

關於 logging 的詳細使用,這裡就不再細說,可以參考官方文件

,或者這裡的介紹。

logging 的配置

通常情況下,我們需要將日誌儲存到檔案中,並期望能自動分割檔案,避免日誌檔案太大。下面給出了一個 logging 的配置例子。

import logging.config

logging.config.dictConfig({
    'version': 1,
    'disable_existing_loggers': True,
    'formatters': {
        'verbose': {
            'format': "[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(message)s",
            'datefmt': "%Y-%m-%d %H:%M:%S"
        },
        'simple': {
            'format': '%(levelname)s %(message)s'
        },
    },
    'handlers': {
        'null': {
            'level': 'DEBUG',
            'class': 'logging.NullHandler',
        },
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'verbose'
        },
        'file': {
            'level': 'DEBUG',
            'class': 'logging.RotatingFileHandler',
            # 當達到10MB時分割日誌
            'maxBytes': 1024 * 1024 * 10,
            # 最多保留50份檔案
            'backupCount': 50,
            # If delay is true,
            # then file opening is deferred until the first call to emit().
            'delay': True,
            'filename': 'logs/mysite.log',
            'formatter': 'verbose'
        }
    },
    'loggers': {
        '': {
            'handlers': ['file'],
            'level': 'info',
        },
    }
})

我們在一個模組內,就可以這麼使用來記錄日誌

import logging
logger = logging.getLogger(__name__)

if __name__ == '__main__':
    logger.info('log info')

多程序環境下的使用

按照官方文件的介紹,logging 是執行緒安全的,也就是說,在一個程序內的多個執行緒同時往同一個檔案寫日誌是安全的。但是(對,這裡有個但是)多個程序往同一個檔案寫日誌不是安全的。官方的說法是這樣的:

Because there is no standard way to serialize access to a single file across multiple processes in Python. If you need to log to a single file from multiple processes, one way of doing this is to have all the processes log to a SocketHandler, and have a separate process which implements a socket server which reads from the socket and logs to file. (If you prefer, you can dedicate one thread in one of the existing processes to perform this function.)

有的人會說,那我不用多程序不就可以了。但是 Python 有一個 GIL 的大鎖(關於 GIL 的糾葛可以看這裡),使用多執行緒是沒法利用到多核 CPU 的,大部分情況下會改用多程序來利用多核 CPU,因此我們還是繞不開不開多程序下日誌的問題。

為了解決這個問題,可以使用 ConcurrentLogHandler,ConcurrentLogHandler 可以在多程序環境下安全的將日誌寫入到同一個檔案,並且可以在日誌檔案達到特定大小時,分割日誌檔案。在預設的 logging 模組中,有個 TimedRotatingFileHandler 類,可以按時間分割日誌檔案,可惜 ConcurrentLogHandler 不支援這種按時間分割日誌檔案的方式。

重新修改下 handlers 中的 class。

logging.config.dictConfig({
    ...
    'handlers': {
        'file': {
            'level': 'DEBUG',
            # 如果沒有使用併發的日誌處理類,在多例項的情況下日誌會出現缺失
            'class': 'cloghandler.ConcurrentRotatingFileHandler',
            # 當達到10MB時分割日誌
            'maxBytes': 1024 * 1024 * 10,
            # 最多保留50份檔案
            'backupCount': 50,
            # If delay is true,
            # then file opening is deferred until the first call to emit().
            'delay': True,
            'filename': 'logs/mysite.log',
            'formatter': 'verbose'
        }
    },
    ...
})

執行後可以發現,會自動建立一個.lock檔案,通過鎖的方式來安全的寫日誌檔案。