Python日誌處理(引用logging模塊)

Python日誌處理

一. 日誌

1. 日誌的作用

在部署項目時,不可能直接將所有的信息都輸出到控制檯中,我們可以將這些信息記錄到日誌文件中,這樣不僅方便我們查看程序運行時的情況,也可以在項目出現故障時根據運行時產生的日誌快速定位問題出現的位置。

日誌是一種可以追蹤某些軟件運行時所發生事件的方法。軟件開發人員可以向他們的代碼中調用日誌記錄相關的方法來表明發生了某些事情。一個事件可以用一個可包含可選變量數據的消息來描述。此外,事件也有重要性的概念,這個重要性也可以被稱爲嚴重性級別(level)。

2. 日誌級別

Python 標準庫 logging 用作記錄日誌,默認分爲六種日誌級別(括號爲級別對應的數值),

  • NOTSET(0)、
  • DEBUG(10)、
  • INFO(20)、
  • WARNING(30)、
  • ERROR(40)、
  • CRITICAL(50)。

當然也可以自己定義級別。

logging 執行時輸出大於等於設置的日誌級別的日誌信息,如設置日誌級別是 INFO,則 INFO、WARNING、ERROR、CRITICAL 級別的日誌都會輸出。

這樣根據等級篩選可以避免我們在排查故障時被淹沒在日誌的海洋裏。

3.日誌的流程

在這裏插入圖片描述
類型說明:

Logger:日誌,暴露函數給應用程序,基於日誌記錄器和過濾器級別決定哪些日誌有效。

LogRecord :日誌記錄器,將日誌傳到相應的處理器處理。

Handler :處理器, 將(日誌記錄器產生的)日誌記錄發送至合適的目的地。

Filter :過濾器, 提供了更好的粒度控制,它可以決定輸出哪些日誌記錄。

Formatter:格式化器, 指明瞭最終輸出中日誌記錄的佈局。

  1. 判斷 Logger 對象對於設置的級別是否可用,如果可用,則往下執行,否則,流程結束。
  2. 創建 LogRecord 對象,如果註冊到 Logger 對象中的 Filter 對象過濾後返回 False,則不記錄日誌,流程結束,否則,則向下執行。
  3. LogRecord 對象將 Handler 對象傳入當前的 Logger 對象,(圖中的子流程)如果 Handler 對象的日誌級別大於設置的日誌級別,再判斷註冊到 Handler 對象中的 Filter 對象過濾後是否返回 True 而放行輸出日誌信息,否則不放行,流程結束。
  4. 如果傳入的 Handler 大於 Logger 中設置的級別,也即 Handler 有效,則往下執行,否則,流程結束。
  5. 判斷這個 Logger 對象是否還有父 Logger 對象,如果沒有(代表當前 Logger 對象是最頂層的 Logger 對象 root Logger),流程結束。否則將 Logger 對象設置爲它的父 Logger 對象,重複上面的 3、4 兩步,輸出父類 Logger 對象中的日誌輸出,直到是 root Logger 爲止。

4. 日誌輸出格式

日誌的輸出格式可以認爲設置,默認格式爲下圖所示。
在這裏插入圖片描述
一個事件通常需要包括以下幾個內容:

  • 事件發生時間
  • 事件發生位置
  • 事件的嚴重程度–日誌級別
  • 事件內容

上面這些都是一條日誌記錄中可能包含的字段信息,當然還可以包括一些其他信息,如進程ID、進程名稱、線程ID、線程名稱等。

輸出一條日誌時,日誌內容和日誌級別是需要開發人員明確指定的。對於而其它字段信息,只需要是否顯示在日誌中就可以了。

5. 日誌功能的實現

Python自身提供了一個用於記錄日誌的標準庫模塊–logging。

二. logging模塊簡介

1. logging模塊的日誌級別

ogging模塊默認定義了以下幾個日誌等級,它允許開發人員自定義其他日誌級別。

日誌等級(level) 描述
DEBUG 最詳細的日誌信息,典型應用場景是 問題診斷
INFO 信息詳細程度僅次於DEBUG,通常只記錄關鍵節點信息,用於確認一切都是按照我們預期的那樣進行工作
WARNING 當某些不期望的事情發生時記錄的信息(如,磁盤可用空間較低),但是此時應用程序還是正常運行的
ERROR 由於一個更嚴重的問題導致某些功能不能正常運行時記錄的信息
CRITICAL 當發生嚴重錯誤,導致應用程序不能繼續運行時記錄的信息

開發應用程序或部署開發環境時,可以使用DEBUG或INFO級別的日誌獲取儘可能詳細的日誌信息來進行開發或部署調試;
應用上線或部署生產環境時,應該使用WARNING或ERROR或CRITICAL級別的日誌來降低機器的I/O壓力和提高獲取錯誤日誌信息的效率。

日誌級別的指定通常都是在應用程序的配置文件中進行指定的。指定日誌記錄器的日誌級別,只有級別大於或等於該指定日誌級別的日誌記錄纔會被輸出,小於該等級的日誌記錄將會被丟棄。

2. logging模塊的使用方式介紹

logging模塊提供了兩種記錄日誌的方式:

  • 第一種方式是使用logging提供的模塊級別的函數
  • 第二種方式是使用Logging日誌系統的四大組件

其實,logging所提供的模塊級別的日誌記錄函數也是對logging日誌系統相關類的封裝而已。

logging模塊定義的模塊級別的常用函數
函數 說明
logging.debug(msg, *args, **kwargs) 創建一條嚴重級別爲DEBUG的日誌記錄
logging.info(msg, *args, **kwargs) 創建一條嚴重級別爲INFO的日誌記錄
logging.warning(msg, *args, **kwargs) 創建一條嚴重級別爲WARNING的日誌記錄
logging.error(msg, *args, **kwargs) 創建一條嚴重級別爲ERROR的日誌記錄
logging.critical(msg, *args, **kwargs) 創建一條嚴重級別爲CRITICAL的日誌記錄
logging.log(level, *args, **kwargs) 創建一條嚴重級別爲level的日誌記錄
logging.basicConfig(**kwargs) 對root logger進行一次性配置

其中logging.basicConfig(**kwargs)函數用於指定“要記錄的日誌級別”、“日誌格式”、“日誌輸出位置”、“日誌文件的打開模式”等信息,其他幾個都是用於記錄各個級別日誌的函數。

logging模塊的四大組件
組件 說明
loggers 提供應用程序代碼直接使用的接口
handlers 用於將日誌記錄發送到指定的目的位置
filters 提供更細粒度的日誌過濾功能,用於決定哪些日誌記錄將會被輸出(其它的日誌記錄將會被忽略)
formatters 用於控制日誌信息的最終輸出格式

說明: logging模塊提供的模塊級別的那些函數實際上也是通過這幾個組件的相關實現類來記錄日誌的,只是在創建這些類的實例時設置了一些默認值。
logging模塊就是通過這些組件來完成日誌處理的,上面所使用的logging模塊級別的函數也是通過這些組件對應的類來實現的。

這些組件之間的關係描述:
  • 日誌器(logger)需要通過處理器(handler)將日誌信息輸出到目標位置,如:文件、sys.stdout、網絡等;
  • 不同的處理器(handler)可以將日誌輸出到不同的位置;
  • 日誌器(logger)可以設置多個處理器(handler)將同一條日誌記錄輸出到不同的位置;
  • 每個處理器(handler)都可以設置自己的過濾器(filter)實現日誌過濾,從而只保留感興趣的日誌;
  • 每個處理器(handler)都可以設置自己的格式器(formatter)實現同一條日誌以不同的格式輸出到不同的地方。

簡單點說就是:日誌器(logger)是入口,真正幹活兒的是處理器(handler),處理器(handler)還可以通過過濾器(filter)和格式器(formatter)對要輸出的日誌內容做過濾和格式化等處理操作。

3. logging模塊基本使用

logging 使用非常簡單,使用 basicConfig() 方法就能滿足基本的使用需要,如果方法沒有傳入參數,會根據默認的配置創建Logger 對象,默認的日誌級別被設置爲 WARNING,默認的日誌輸出格式如上圖,該函數可選的參數如下表所示。

參數名稱 參數描述
filename 日誌輸出到文件的文件名
filemode 文件模式,r[+]、w[+]、a[+]
format 日誌輸出的格式
datefat 日誌附帶日期時間的格式
style 格式佔位符,默認爲 “%” 和 “{}”
level 設置日誌輸出級別
stream 定義輸出流,用來初始化 StreamHandler 對象,不能 filename 參數一起使用,否則會ValueError 異常
handles 定義處理器,用來創建 Handler 對象,不能和 filename 、stream 參數一起使用,否則也會拋出 ValueError 異常

示例代碼如下:

import logging

logging.basicConfig()
logging.debug('This is a debug message')
logging.info('This is an info message')
logging.warning('This is a warning message')
logging.error('This is an error message')
logging.critical('This is a critical message')

輸出結果如下:

WARNING:root:This is a warning message
ERROR:root:This is an error message
CRITICAL:root:This is a critical message

爲什麼前面兩條日誌沒有被打印出來?
這是因爲logging模塊提供的日誌記錄函數所使用的日誌器設置的日誌級別是WARNING,因此只有WARNING級別的日誌記錄以及大於它的ERROR和CRITICAL級別的日誌記錄被輸出了,而小於它的DEBUG和INFO級別的日誌記錄被丟棄了。
打印出來的日誌信息中各字段表示什麼意思?爲什麼會這樣輸出?
上面輸出結果中每行日誌記錄的各個字段含義分別是:

日誌級別:日誌器名稱:日誌內容

之所以會這樣輸出,是因爲logging模塊提供的日誌記錄函數所使用的日誌器設置的日誌格式默認是BASIC_FORMAT,其值爲:

“%(levelname)s:%(name)s:%(message)s”

如果將日誌記錄輸出到文件中,而不是打印到控制檯?
因爲在logging模塊提供的日誌記錄函數所使用的日誌器設置的處理器所指定的日誌輸出位置默認爲:

sys.stderr。

傳入常用的參數,示例代碼如下:

import logging

logging.basicConfig(filename="test.log", filemode="w", format="%(asctime)s %(name)s:%(levelname)s:%(message)s", datefmt="%d-%m-%Y %H:%M:%S", level=logging.DEBUG)
logging.debug('This is a debug message')
logging.info('This is an info message')
logging.warning('This is a warning message')
logging.error('This is an error message')
logging.critical('This is a critical message')

生成的日誌文件 test.log ,內容如下:

13-10-18 21:10:32 root:DEBUG:This is a debug message
13-10-18 21:10:32 root:INFO:This is an info message
13-10-18 21:10:32 root:WARNING:This is a warning message
13-10-18 21:10:32 root:ERROR:This is an error message
13-10-18 21:10:32 root:CRITICAL:This is a critical message

但是當發生異常時,直接使用無參數的 debug()、info()、warning()、error()、critical() 方法並不能記錄異常信息,需要設置 exc_info 參數爲 True 纔可以,或者使用 exception() 方法,還可以使用 log() 方法,但還要設置日誌級別和 exc_info 參數。

import logging

logging.basicConfig(filename="test.log", filemode="w", format="%(asctime)s %(name)s:%(levelname)s:%(message)s", datefmt="%d-%M-%Y %H:%M:%S", level=logging.DEBUG)
a = 5
b = 0
try:
    c = a / b
except Exception as e:
    # 下面三種方式三選一,推薦使用第一種
    logging.exception("Exception occurred")
    logging.error("Exception occurred", exc_info=True)
    logging.log(level=logging.DEBUG, msg="Exception occurred", exc_info=True)

4. logging模塊定義的格式字符串字段

字段/屬性名稱 使用格式 描述
asctime %(asctime)s 日誌事件發生的時間–人類可讀時間,如:2003-07-08 16:49:45,896
created %(created)f 日誌事件發生的時間–時間戳,就是當時調用time.time()函數返回的值
relativeCreated %(relativeCreated)d 日誌事件發生的時間相對於logging模塊加載時間的相對毫秒數(目前還不知道幹嘛用的)
msecs %(msecs)d 日誌事件發生事件的毫秒部分
levelname %(levelname)s 該日誌記錄的文字形式的日誌級別(‘DEBUG’, ‘INFO’, ‘WARNING’, ‘ERROR’, ‘CRITICAL’)
levelno %(levelno)s 該日誌記錄的數字形式的日誌級別(10, 20, 30, 40, 50)
name %(name)s 所使用的日誌器名稱,默認是’root’,因爲默認使用的是 rootLogger
message %(message)s 日誌記錄的文本內容,通過 msg % args計算得到的
pathname %(pathname)s 調用日誌記錄函數的源碼文件的全路徑
filename %(filename)s pathname的文件名部分,包含文件後綴
module %(module)s filename的名稱部分,不包含後綴
lineno %(lineno)d 調用日誌記錄函數的源代碼所在的行號
funcName %(funcName)s 調用日誌記錄函數的函數名
process %(process)d 進程ID
processName %(processName)s 進程名稱,Python 3.1新增
thread %(thread)d 線程ID
threadName %(thread)s 線程名稱

5. 經過配置的日誌輸出

先簡單配置下日誌器的日誌級別

logging.basicConfig(level=logging.DEBUG)

logging.debug("This is a debug log.")
logging.info("This is a info log.")
logging.warning("This is a warning log.")
logging.error("This is a error log.")
logging.critical("This is a critical log.")

輸出結果:

DEBUG:root:This is a debug log.
INFO:root:This is a info log.
WARNING:root:This is a warning log.
ERROR:root:This is a error log.
CRITICAL:root:This is a critical log.

所有等級的日誌信息都被輸出了,說明配置生效了。

在配置日誌器日誌級別的基礎上,在配置下日誌輸出目標文件和日誌格式

LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s"
logging.basicConfig(filename='my.log', level=logging.DEBUG, format=LOG_FORMAT)

logging.debug("This is a debug log.")
logging.info("This is a info log.")
logging.warning("This is a warning log.")
logging.error("This is a error log.")
logging.critical("This is a critical log.")

此時會發現控制檯中已經沒有輸出日誌內容了,但是在python代碼文件的相同目錄下會生成一個名爲’my.log’的日誌文件,該文件中的內容爲:

2017-05-08 14:29:53,783 - DEBUG - This is a debug log.
2017-05-08 14:29:53,784 - INFO - This is a info log.
2017-05-08 14:29:53,784 - WARNING - This is a warning log.
2017-05-08 14:29:53,784 - ERROR - This is a error log.
2017-05-08 14:29:53,784 - CRITICAL - This is a critical log.

在上面的基礎上,我們再來設置下日期/時間格式

LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s"
DATE_FORMAT = "%m/%d/%Y %H:%M:%S %p"

logging.basicConfig(filename='my.log', level=logging.DEBUG, format=LOG_FORMAT, datefmt=DATE_FORMAT)

logging.debug("This is a debug log.")
logging.info("This is a info log.")
logging.warning("This is a warning log.")
logging.error("This is a error log.")
logging.critical("This is a critical log.")

此時會在my.log日誌文件中看到如下輸出內容:

05/08/2017 14:29:04 PM - DEBUG - This is a debug log.
05/08/2017 14:29:04 PM - INFO - This is a info log.
05/08/2017 14:29:04 PM - WARNING - This is a warning log.
05/08/2017 14:29:04 PM - ERROR - This is a error log.
05/08/2017 14:29:04 PM - CRITICAL - This is a critical log.

掌握了上面的內容之後,已經能夠滿足我們平時開發中需要的日誌記錄功能。

6、自定義 Logger

上面的基本使用可以讓我們快速上手 logging 模塊,但一般並不能滿足實際使用,我們還需要自定義 Logger。

一個系統只有一個 Logger 對象,並且該對象不能被直接實例化,沒錯,這裏用到了單例模式,獲取 Logger 對象的方法爲 getLogger。

注意:這裏的單例模式並不是說只有一個 Logger 對象,而是指整個系統只有一個根 Logger 對象,Logger 對象在執行 info()、error() 等方法時實際上調用都是根 Logger 對象對應的 info()、error() 等方法。

我們可以創造多個 Logger 對象,但是真正輸出日誌的是根 Logger 對象。每個 Logger 對象都可以設置一個名字,如果設置logger = logging.getLogger(name),name 是 Python 中的一個特殊內置變量,他代表當前模塊的名稱(默認爲 main)。則 Logger 對象的 name 爲建議使用使用以點號作爲分隔符的命名空間等級制度。

Logger 對象可以設置多個 Handler 對象和 Filter 對象,Handler 對象又可以設置 Formatter 對象。

7. 其他說明

幾個要說明的內容:
  • logging.basicConfig()函數是一個一次性的簡單配置工具使,也就是說只有在第一次調用該函數時會起作用,後續再次調用該函數時完全不會產生任何操作的,多次調用的設置並不是累加操作。
  • 日誌器(Logger)是有層級關係的,上面調用的logging模塊級別的函數所使用的日誌器是RootLogger類的實例,其名稱爲’root’,它是處於日誌器層級關係最頂層的日誌器,且該實例是以單例模式存在的。
  • 如果要記錄的日誌中包含變量數據,可使用一個格式字符串作爲這個事件的描述消息(logging.debug、logging.info等函數的第一個參數),然後將變量數據作爲第二個參數*args的值進行傳遞,如:logging.warning('%s is %d years old.', 'Tom', 10),輸出內容爲WARNING:root:Tom is 10 years old.
  • logging.debug(), logging.info()等方法的定義中,除了msg和args參數外,還有一個**kwargs參數。它們支持3個關鍵字參數: exc_info, stack_info, extra,下面對這幾個關鍵字參數作個說明。
關於exc_info, stack_info, extra關鍵詞參數的說明:
  • exc_info: 其值爲布爾值,如果該參數的值設置爲True,則會將異常異常信息添加到日誌消息中。如果沒有異常信息則添加None到日誌信息中。
  • stack_info: 其值也爲布爾值,默認值爲False。如果該參數的值設置爲True,棧信息將會被添加到日誌信息中。
  • extra: 這是一個字典(dict)參數,它可以用來自定義消息格式中所包含的字段,但是它的key不能與logging模塊定義的字段衝突。
一個例子:

在日誌消息中添加exc_info和stack_info信息,並添加兩個自定義的字端 ip和user

LOG_FORMAT = "%(asctime)s - %(levelname)s - %(user)s[%(ip)s] - %(message)s"
DATE_FORMAT = "%m/%d/%Y %H:%M:%S %p"

logging.basicConfig(format=LOG_FORMAT, datefmt=DATE_FORMAT)
logging.warning("Some one delete the log file.", exc_info=True, stack_info=True, extra={'user': 'Tom', 'ip':'47.98.53.222'})

輸出結果:

05/08/2017 16:35:00 PM - WARNING - Tom[47.98.53.222] - Some one delete the log file.
NoneType
Stack (most recent call last):
  File "C:/Users/wader/PycharmProjects/LearnPython/day06/log.py", line 45, in <module>
    logging.warning("Some one delete the log file.", exc_info=True, stack_info=True, extra={'user': 'Tom', 'ip':'47.98.53.222'})

四. logging日誌模塊相關類及其常用方法介紹

下面介紹下與logging四大組件相關的類:Logger, Handler, Filter, Formatter。

Logger類

Logger對象有3個任務要做:

  • 1)嚮應用程序代碼暴露幾個方法,使應用程序可以在運行時記錄日誌消息;
  • 2)基於日誌嚴重等級(默認的過濾設施)或filter對象來決定要對哪些日誌進行後續處理;
  • 3)將日誌消息傳送給所有感興趣的日誌handlers。

Logger對象最常用的方法分爲兩類:配置方法 和 消息發送方法

最常用的配置方法如下:

方法 描述
Logger.setLevel() 設置日誌器將會處理的日誌消息的最低嚴重級別
Logger.addHandler() 和 Logger.removeHandler() 爲該logger對象添加 和 移除一個handler對象
Logger.addFilter() 和 Logger.removeFilter() 爲該logger對象添加 和 移除一個filter對象

關於Logger.setLevel()方法的說明:

內建等級中,級別最低的是DEBUG,級別最高的是CRITICAL。例如setLevel(logging.INFO),此時函數參數爲INFO,那麼該logger將只會處理INFO、WARNING、ERROR和CRITICAL級別的日誌,而DEBUG級別的消息將會被忽略/丟棄。

logger對象配置完成後,可以使用下面的方法來創建日誌記錄:

方法 描述
Logger.debug(), Logger.info(), Logger.warning(), Logger.error(), Logger.critical() 創建一個與它們的方法名對應等級的日誌記錄
Logger.exception() 創建一個類似於Logger.error()的日誌消息
Logger.log() 需要獲取一個明確的日誌level參數來創建一個日誌記錄

說明:

  • Logger.exception()與Logger.error()的區別在於:Logger.exception()將會輸出堆棧追蹤信息,另外通常只是在一個exception handler中調用該方法。
  • Logger.log()與Logger.debug()、Logger.info()等方法相比,雖然需要多傳一個level參數,顯得不是那麼方便,但是當需要記錄自定義level的日誌時還是需要該方法來完成。

那麼,怎樣得到一個Logger對象呢?一種方式是通過Logger類的實例化方法創建一個Logger類的實例,但是我們通常都是用第二種方式–logging.getLogger()方法。

logging.getLogger()方法有一個可選參數name,該參數表示將要返回的日誌器的名稱標識,如果不提供該參數,則其值爲’root’。若以相同的name參數值多次調用getLogger()方法,將會返回指向同一個logger對象的引用。

關於logger的層級結構與有效等級的說明:

  • logger的名稱是一個以’.‘分割的層級結構,每個’.‘後面的logger都是’.'前面的logger的children,例如,有一個名稱爲 foo 的logger,其它名稱分別爲 foo.bar, foo.bar.baz 和 foo.bam都是 foo 的後代。
  • logger有一個"有效等級(effective level)"的概念。如果一個logger上沒有被明確設置一個level,那麼該logger就是使用它parent的level;如果它的parent也沒有明確設置level則繼續向上查找parent的parent的有效level,依次類推,直到找到個一個明確設置了level的祖先爲止。需要說明的是,root logger總是會有一個明確的level設置(默認爲 WARNING)。當決定是否去處理一個已發生的事件時,logger的有效等級將會被用來決定是否將該事件傳遞給該logger的handlers進行處理。
  • child loggers在完成對日誌消息的處理後,默認會將日誌消息傳遞給與它們的祖先loggers相關的handlers。因此,我們不必爲一個應用程序中所使用的所有loggers定義和配置handlers,只需要爲一個頂層的logger配置handlers,然後按照需要創建child loggers就可足夠了。我們也可以通過將一個logger的propagate屬性設置爲False來關閉這種傳遞機制。

Handler類

Handler對象的作用是(基於日誌消息的level)將消息分發到handler指定的位置(文件、網絡、郵件等)。Logger對象可以通過addHandler()方法爲自己添加0個或者更多個handler對象。比如,一個應用程序可能想要實現以下幾個日誌需求:

  • 1)把所有日誌都發送到一個日誌文件中;
  • 2)把所有嚴重級別大於等於error的日誌發送到stdout(標準輸出);
  • 3)把所有嚴重級別爲critical的日誌發送到一個email郵件地址。
    這種場景就需要3個不同的handlers,每個handler複雜發送一個特定嚴重級別的日誌到一個特定的位置。

一個handler中只有非常少數的方法是需要應用開發人員去關心的。對於使用內建handler對象的應用開發人員來說,似乎唯一相關的handler方法就是下面這幾個配置方法:

方法 描述
Handler.setLevel() 設置handler將會處理的日誌消息的最低嚴重級別
Handler.setFormatter() 爲handler設置一個格式器對象
Handler.addFilter() 和 Handler.removeFilter() 爲handler添加 和 刪除一個過濾器對象

需要說明的是,應用程序代碼不應該直接實例化和使用Handler實例。因爲Handler是一個基類,它只定義了素有handlers都應該有的接口,同時提供了一些子類可以直接使用或覆蓋的默認行爲。下面是一些常用的Handler:

Handler 描述
logging.StreamHandler 將日誌消息發送到輸出到Stream,如std.out, std.err或任何file-like對象。
logging.FileHandler 將日誌消息發送到磁盤文件,默認情況下文件大小會無限增長
logging.handlers.RotatingFileHandler 將日誌消息發送到磁盤文件,並支持日誌文件按大小切割
logging.hanlders.TimedRotatingFileHandler 將日誌消息發送到磁盤文件,並支持日誌文件按時間切割
logging.handlers.HTTPHandler 將日誌消息以GET或POST的方式發送給一個HTTP服務器
logging.handlers.SMTPHandler 將日誌消息發送給一個指定的email地址
logging.NullHandler 該Handler實例會忽略error messages,通常被想使用logging的library開發者使用來避免’No handlers could be found for logger XXX’信息的出現。

Formater類

Formater對象用於配置日誌信息的最終順序、結構和內容。與logging.Handler基類不同的是,應用代碼可以直接實例化Formatter類。另外,如果你的應用程序需要一些特殊的處理行爲,也可以實現一個Formatter的子類來完成。

Formatter類的構造方法定義如下:

logging.Formatter.__init__(fmt=None, datefmt=None, style='%')

可見,該構造方法接收3個可選參數:

  • fmt:指定消息格式化字符串,如果不指定該參數則默認使用message的原始值
  • datefmt:指定日期格式字符串,如果不指定該參數則默認使用"%Y-%m-%d %H:%M:%S"
  • style:Python 3.2新增的參數,可取值爲 ‘%’, ‘{‘和 ‘$’,如果不指定該參數則默認使用’%’

Filter類

Filter可以被Handler和Logger用來做比level更細粒度的、更復雜的過濾功能。Filter是一個過濾器基類,它只允許某個logger層級下的日誌事件通過過濾。該類定義如下:

class logging.Filter(name='')
    filter(record)

比如,一個filter實例化時傳遞的name參數值爲’A.B’,那麼該filter實例將只允許名稱爲類似如下規則的loggers產生的日誌記錄通過過濾:‘A.B’,‘A.B,C’,‘A.B.C.D’,‘A.B.D’,而名稱爲’A.BB’, 'B.A.B’的loggers產生的日誌則會被過濾掉。如果name的值爲空字符串,則允許所有的日誌事件通過過濾。

filter方法用於具體控制傳遞的record記錄是否能通過過濾,如果該方法返回值爲0表示不能通過過濾,返回值爲非0表示可以通過過濾。

說明:

  • 如果有需要,也可以在filter(record)方法內部改變該record,比如添加、刪除或修改一些屬性。
  • 我們還可以通過filter做一些統計工作,比如可以計算下被一個特殊的logger或handler所處理的record數量等。

五、使用logging四大組件記錄日誌


現在,我們對logging模塊的重要組件及整個日誌流處理流程都應該有了一個比較全面的瞭解,下面我們來看一個例子。

1. 需求

現在有以下幾個日誌記錄的需求:

  • 1)要求將所有級別的所有日誌都寫入磁盤文件中
  • 2)all.log文件中記錄所有的日誌信息,日誌格式爲:日期和時間 - 日誌級別 - 日誌信息
  • 3)error.log文件中單獨記錄error及以上級別的日誌信息,日誌格式爲:日期和時間 - 日誌級別 - 文件名[:行號] - 日誌信息
  • 4)要求all.log在每天凌晨進行日誌切割

2. 分析

  • 1)要記錄所有級別的日誌,因此日誌器的有效level需要設置爲最低級別–DEBUG;
  • 2)日誌需要被髮送到兩個不同的目的地,因此需要爲日誌器設置兩個handler;另外,兩個目的地都是磁盤文件,因此這兩個handler都是與FileHandler相關的;
  • 3)all.log要求按照時間進行日誌切割,因此他需要用logging.handlers.TimedRotatingFileHandler; 而error.log沒有要求日誌切割,因此可以使用FileHandler;
  • 4)兩個日誌文件的格式不同,因此需要對這兩個handler分別設置格式器;

3. 代碼實現

import logging
import logging.handlers
import datetime

logger = logging.getLogger('mylogger')
logger.setLevel(logging.DEBUG)

rf_handler = logging.handlers.TimedRotatingFileHandler('all.log', when='midnight', interval=1, backupCount=7, atTime=datetime.time(0, 0, 0, 0))
rf_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))

f_handler = logging.FileHandler('error.log')
f_handler.setLevel(logging.ERROR)
f_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(filename)s[:%(lineno)d] - %(message)s"))

logger.addHandler(rf_handler)
logger.addHandler(f_handler)

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

all.log文件輸出

2017-05-13 16:12:40,612 - DEBUG - debug message
2017-05-13 16:12:40,612 - INFO - info message
2017-05-13 16:12:40,612 - WARNING - warning message
2017-05-13 16:12:40,612 - ERROR - error message
2017-05-13 16:12:40,613 - CRITICAL - critical message

error.log文件輸出

2017-05-13 16:12:40,612 - ERROR - log.py[:81] - error message
2017-05-13 16:12:40,613 - CRITICAL - log.py[:82] - critical message

六.參考鏈接

https://www.cnblogs.com/yyds/p/6901864.html
https://juejin.im/post/5bc2bd3a5188255c94465d31
https://docs.python.org/3.5/howto/logging.html
https://docs.python.org/3.5/howto/logging-cookbook.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章