1. 程式人生 > 其它 >Python-logging模組實現同時向控制檯和檔案列印日誌

Python-logging模組實現同時向控制檯和檔案列印日誌

Python-logging模組實現同時向控制檯和檔案列印日誌

前言

在寫我自己的練手專案的時候,需要寫一系列Python指令碼來幫助我進行運維/環境配置,我希望這些指令碼能夠有比較好的日誌記錄。

一方面我希望其能夠直接列印到控制檯,方便我實時檢視,另一方面我也希望能夠記錄到日誌檔案中,這樣能夠方便我日後翻舊賬。

更進一步,我希望控制檯輸出比較重要的資訊,讓我的控制檯不至於太亂,而日誌檔案中我希望能夠儘量詳細。

參考

basicConfig

參考Python 官方文件 | logging模組#basicConfig

這個函式對logging模組的日誌記錄器進行基本配置,無論我們直接通過logging.info(msg)進行日誌記錄,還是我們先通過logging.getLogger(name)獲取一個logger再進行日誌記錄,通過basicConfig函式加入的配置都會生效。

值得注意的是,因為有些操作會導致basicConfig函式被自動呼叫,所以我們儘量在程式開始時儘早進行呼叫。

我們需要關注的是basicConfighandlers引數。

handler

如果說logger是面向上層開發者的介面,那麼handler就是面向底層的執行者。開發者通過呼叫logger的方法來輸出日誌,而logger通過handler將日誌進行實際的記錄。

一個logger可以有若干handler,logger和handler都可以有一個日誌等級。當記錄等級高於logger的日誌時,logger會通知每一個handler對該日誌進行記錄,而每一個handler記錄會通過該條日誌的等級和handler的等級進行比較,當日志等級高於handler等級的時候,才會進行記錄。

handler主要位於logging.handler包下,我們這次需要使用StreamHandler以及其子類FileHandler

實現

通過前面的分析,思路已經很清晰了:

  1. 建立兩個handler,其中一個向控制檯進行輸出,一個向檔案進行輸出;
  2. 為這兩個handler配置不同的日誌等級和日誌格式;
  3. 通過basicConfig函式來將這兩個handler新增到全域性日誌的預設配置;

以下為實現:

import logging
import sys

def config_logging(file_name: str, console_level: int=logging.INFO, file_level: int=logging.DEBUG):
    file_handler = logging.FileHandler(file_name, mode='a', encoding="utf8")
    file_handler.setFormatter(logging.Formatter(
        '%(asctime)s [%(levelname)s] %(module)s.%(lineno)d %(name)s:\t%(message)s')
        )
    file_handler.setLevel(file_level)

    console_handler = logging.StreamHandler(sys.stdout)
    console_handler.setFormatter(logging.Formatter(
        '[%(asctime)s %(levelname)s] %(message)s',
        datefmt="%Y/%m/%d %H:%M:%S")
        )
    console_handler.setLevel(console_level)

    logging.basicConfig(
        level=min(console_level, file_level),
        handlers=[file_handler, console_handler],
        )

if __name__ == '__main__':
    config_logging("test.log", logging.WARNING, logging.DEBUG)
    logging.debug("debug")
    logging.info("info")
    logging.warning("warning")
    logging.critical("critical")

    logger = logging.getLogger(__name__)
    logger.debug("debug")
    logger.info("info")
    logger.warning("warning")
    logger.critical("critical")

關鍵是config_logging函式,這個函式進行了基本的配置,我們只需要在程式的入口處先呼叫這個函式就能完成全域性的日誌配置。

需要注意的是在呼叫logging.basicConfig時,我們需要設定level=min(console_level, file_level),這是因為這個引數設定的是logger的等級,如果一條日誌已經被logger過濾掉了,那麼handler的等級設定的再低也不會進行記錄。直接level=logging.DEBUGlevel=0應該也可以,但是我認為比所有handler的等級都低的日誌是不會進行輸出的,與其讓這條日誌到了handler這一步才被過濾,不如直接就在logger這一步就將其過濾掉。