Python logging(日誌)模塊

python日誌模塊

內容簡介

1.日誌相關概念 2.logging模塊簡介 3.logging模塊函數使用 4.logging模塊日誌流處理流程 5.logging模塊組件使用 6.logging配置方式 7.日誌輸出中添加上下文信息php

1、日誌相關概念

日誌是一種能夠追蹤軟件運行時所發生事件的方法,軟件開發人員能夠向其代碼中調用日誌記錄相關的方法來代表發生了某些事情。一個事件能夠用一個包含可選變量數據的消息來描述,此外事件也有重要性的概念,這個重要性也能夠被稱爲嚴重級別(level)。 1.日誌的做用 經過日誌的分析,能夠方便用戶瞭解系統或軟件、應用的運行狀況,若是咱們的引用日誌足夠豐富,也能夠分析以往用戶的操做行爲、類型喜愛、地域分佈或其餘更多信息;若是一個應用的日誌同時也分了多個級別,那麼能夠很輕鬆地分析獲得該應用的健康情況,及時發現問題並快速定位、解決問題。簡單來說就是,咱們經過記錄和分析日誌能夠了解一個系統或軟件程序運行狀況是否正常,也能夠在應用程序出現故障時快速定位問題。好比,作運維的同窗,在接收到報警或各類問題反饋後,進行問題排查時一般都會先去看各類日誌。對於運維老司機或有經驗的開發人員,能夠快速的經過日誌定位到問題的根源,可見日誌的重要性。日誌的做用能夠簡單總結爲下面幾點:html

  • 調試程序;
  • 瞭解應用軟件程序運行是否正常;
  • 軟件程序故障分析與問題定位;

若是應用軟件程序的日誌信息足夠詳細和豐富,還能夠用來作用戶行爲分析,如:分析用戶的操做行爲、類型喜愛、地域分佈以及其餘更多的信息,由此以實現業務改進、提升商業利益。python

2.日誌的級別 咱們先來思考下如下兩個問題:nginx

  • 做爲開發人員,在開發一個應用程序的時候須要什麼日誌信息?在應用程序正式上線後須要什麼日誌信息?
  • 做爲應用運維人員,在部署開發環境時須要什麼日誌信息?在部署生產環境時須要什麼日誌信息?

在軟件開發階段或部署開發環境時,爲了儘量詳細的查看應用程序的運行狀態來保證上線後的穩定性,咱們可能須要把該引用程序全部的運行日誌所有記錄下來進行分析,這是很是耗費機器性能的。但應用程序正式發佈或在生產環境部署應用程序時,咱們一般只須要記錄應用程序的異常信息、錯誤信息等,這樣便可以減少服務器的I/O壓力,也能夠避免咱們在排查故障時被淹沒在日誌的海洋裏。那麼,怎麼才能在不改動應用程序代碼的狀況下實如今不一樣環境記錄不一樣詳細程度的日誌呢?這就是日誌級別的做用了,咱們能夠經過配置文件指定咱們須要的日誌級別就能夠了。不一樣的應用程序所定義的日誌級別可能會有所差異,分的詳細點的會包含如下幾個等級:服務器

  • DEBUG
  • INFO
  • NOTICE
  • WARNING
  • ERROR
  • CRITICAL
  • ALERT
  • EMERGENCY

3.日誌字段信息與日誌格式 在開篇提到過,一條日誌信息對應的是一個事件的發生,而一個事件一般須要包括如下幾個內容:網絡

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

上面這些都是一條日誌記錄中可能包含的字段信息,固然還能夠包括一些其餘信息,好比:進程ID、進程名稱、線程ID、線程ID等等。日誌格式就是用來定義一條日誌記錄中包含哪些字段的,且日誌格式一般都是能夠自定義的。運維

輸出一條日誌時,日誌內容和日誌級別是須要開發人員明確指定的。對於其餘字段信息,只須要是否顯示在日誌中就能夠了。函數

4.日誌功能的實現 幾乎全部開發語言都會內置日誌相關功能,或者會有比較優秀的第三方庫來提供日誌操做功能,好比:log4j,log4php等。它們功能強大、使用簡單,Python自身也提供了一個用於記錄日誌的標準庫模塊----logging。工具

2、logging模塊簡介

logging模塊定義的類和函數爲應用程序的開發提供了一個靈活的日誌系統。logging模塊是Python的一個標準庫模塊,由標準庫模塊提供日誌記錄API的關鍵好處是全部Python模塊均可以使用這個日誌系統。因此,咱們的應用日誌能夠將本身的日誌信息與來自第三方模塊的信息整合起來。性能

1.logging模塊的日誌級別 logging模塊默認定義瞭如下幾個日誌級別,它容許開發人員自定義其它日誌級別,可是這是不被推薦的,尤爲是在開發供別人使用的庫時,由於這會致使日誌級別的混亂。

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

開發應用程序或部署開發環境時,可使用DEBUG或INFO級別的日誌獲取儘量詳細的日誌信息來進行開發或部署調試;應用上限或部署生產環境時,應該使用WARNING或ERROR或CRITICAL級別的日誌來下降機器的I/O壓力和提升獲取錯誤日誌信息的效率,日誌級別的指定一般都是在應用程序的配置文件中進行制定的。

  • 上面表中的日誌級別從上到下依次升高,即:DEBUG< INFO< WARNING< ERROR< CRITICAL,而日誌的信息量是依次減小的;
  • 當爲某個應用程序指定一個日誌級別後,應用程序會記錄全部日誌級別大於或等於指定級別的日誌信息,而不是僅僅只記錄指定級別的日誌信息,nginx、php等應用程序以及這裏要提到的Python的logging模塊都是這樣的。一樣,logging模塊也能夠指定日誌記錄器的日誌級別,只有級別大於或等於該指定日誌級別的日誌纔會被輸出,小於該級別的日誌將會被丟棄。

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) 對日誌系統作基本的配置

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

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

logging模塊提供的模塊級別的那些函數實際上也是經過這幾個組件的相關實現類來記錄日誌的,只是在建立這些類的實例時設置了一些默認值。

3、logging模塊函數使用

回顧下前面提到的幾個重要信息:

  • 能夠經過logging模塊定義的模塊級別的方法去完成簡單的日誌記錄;
  • 只有級別大於或等於日誌記錄器指定級別的日誌記錄纔會被輸出,小於該級別的日誌記錄將會被丟棄;

1.最簡單的日誌輸出 先來試着分別輸出一條不一樣日誌級別的日誌記錄:

import logging


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.")

# 也能夠這樣寫:
logging.log(logging.DEBUG, "This is a debug log.")
logging.log(logging.INFO, "This is a info log.")
logging.log(logging.WARNING, "This is a warning log.")
logging.log(logging.ERROR, "This is a error log.")
logging.log(logging.CRITICAL, "This is a critical log.")

運行輸出結果爲:

WARNING:root:This is a warning log.
ERROR:root:This is a error log.
CRITICAL:root:This is a critical log.

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

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

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

"%(levelname)s:%(name)s:%(messsage)s"

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

sys.stderr

問題4.我是怎麼知道這些的? 查看這些日誌記錄函數的實現代碼,能夠發現:當咱們沒有提供任何配置信息的時候,這些函數都會去調用logging.basicConfig(**kwargs)方法,且不會向該方法傳遞任何參數,繼續查看basicConfig(**kwargs)方法的代碼就能夠找到上面這些問題的答案了。 問題5.怎麼修改這些默認設置呢? 其實很簡單,在咱們調用上面這些函日誌記錄函數以前,手動調用一下basicConfig(**kwargs)方法,把咱們想設置的內容以參數的形式傳遞進去就能夠了。

**3.logging.basicConfig(kwargs)函數說明 該方法用於爲logging日誌系統作一些基本配置,方法定義以下:

logging.basicConfig(**kwargs)

該函數能夠接收的關鍵字參數以下:

參數名稱 描述
filename 指定日誌輸出目標文件的文件名,指定該設置項後日志信息就不會被輸出到控制檯了
filemode 指定日誌文件的打開模式,默認爲"a"。須要注意的是,該選項要在filename指定時纔有效
format 指定日誌格式字符串,即指定日誌輸出時所包含的字段信息以及它們的順序,logging模塊定義的格式字段下面會列出
datefmt 指定日期/時間格式。須要注意的是,該選項要在format中包含時間字段%(asctime)時纔有效
level 指定日誌器的日誌級別
stream 指定日誌輸出目標stream,如sys.stdout、sys.stderr以及網絡stream。須要注意的是,stream和filename不能同時提供,不然stream會被忽略
style 指定format格式字符串的風格,可取值爲"%"、"{"和"$",默認爲"%"
handlers 該選項若是被指定,它應該是一個建立了多個Handler的可迭代對象,這些handler將會被添加到root logger。須要注意的是:filename、stream和handler這三個配置項只能有一個存在,不能同時出現2個或3個

4.logging模塊定義的格式字符串字段 下面是logging模塊中定義好的能夠用於format格式字符串中的字段:

字段/屬性名稱 使用格式 描述
asctime %(asctime)s 日誌事件發生的時間--人類可讀時間,如:2018-05-05 19:50:45,9527
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)s 調用日誌記錄函數的源代碼所在的行號
funcName %(funcName)s 調用日誌記錄函數的函數名
process %(process)d 進程ID
processName %(processName)s 進程名稱
thread %(thread)d 線程ID
threadName %(threadName)s 線程名稱

5.通過配置的日誌輸出 先簡單配置下日誌器的日誌級別

import logging

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.

全部等級的日誌信息都被輸出了,說明配置生效了。 在配置日誌器日誌級別的基礎上,在配置日誌輸出目標文件和日誌格式:

import logging

LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s"
logging.basicConfig(level=logging.DEBUG, filename="my.log", 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"的日誌文件,以下圖所示:

該文件的內容爲:

2018-05-05 20:14:12,806 - DEBUG - This is a debug log.
2018-05-05 20:14:12,806 - INFO - This is a info log.
2018-05-05 20:14:12,807 - WARNING - This is a warning log.
2018-05-05 20:14:12,807 - ERROR - This is a error log.
2018-05-05 20:14:12,807 - CRITICAL - This is a critical log.

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

import logging

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

logging.basicConfig(level=logging.DEBUG, filename="my.log",
                    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/05/2018 20:20:22 PM - DEBUG - This is a debug log.
05/05/2018 20:20:22 PM - INFO - This is a info log.
05/05/2018 20:20:22 PM - WARNING - This is a warning log.
05/05/2018 20:20:22 PM - ERROR - This is a error log.
05/05/2018 20:20:22 PM - CRITICAL - This is a critical log.

掌握了上面的內容以後,已經可以知足咱們平時開發中須要的日誌記錄功能。

6.其餘說明 幾個要說明的內容:

  • logging.basicConfig(**kwargs)函數是一個一次性的簡單配置工具,也就是說只有在第一次調用該函數時會起做用,後續再次調用該函數時徹底不會產生任何操做的,屢次調用的設置並非累加操做;
  • 日誌器(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:

import logging

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": "95.27.16.88"})

運行輸出結果爲:

05/05/2018 20:43:58 PM - WARNING - Tom[95.27.16.88] - Some one delete the log file.
NoneType: None
Stack (most recent call last):
  File "D:/PythonCode/module for test/logging module.py", line 8, in <module>
    extra={"user": "Tom", "ip": "95.27.16.88"})

4、logging模塊日誌流處理流程

在介紹logging模塊的高級用法以前,頗有必要對logging模塊所包含的重要組件以及其工做流程作個全面、簡要的介紹,這有助於咱們更好的理解咱們所寫的代碼。 1.logging日誌模塊四大組件 在介紹logging模塊的日誌流處理流程以前,咱們先來介紹下logging模塊的四大組件:

組件名稱 對應類名 功能描述
日誌器 Logger 提供了應用程序可直接使用的接口
處理器 Handler 將logger建立的日誌記錄發送到合適的目的地輸出
過濾器 Filter 提供了更細粒度的控制工具來決定輸出哪條日誌記錄,丟棄哪條日誌記錄
格式器 Formatter 決定日誌記錄的最終輸出格式

logging模塊就是經過這些組件來完成日誌處理的,上面所使用的logging模塊級別的函數也是經過這些組件對應的類來實現的。這些組件之間的關係描述:

  • 日誌器(Logger)須要經過處理器(Handler)將日誌信息輸出到目標位置,如:文件、sys.stdout、網絡等;
  • 不一樣的處理器(Handler)能夠將日誌輸出到不一樣的位置;
  • 日誌器(Logger)能夠設置多個處理器(Handler)將同一條日誌記錄輸出到不一樣的位置;
  • 每一個處理器(Handler)均可以設置本身的過濾器(Filter)實現日誌過濾,從而保留感興趣的日誌;
  • 每一個處理器(Handler)均可以設置本身的格式器(Formatter)實現同一條日誌以不一樣的格式輸出到不一樣的地方;

簡單點說就是:日誌器(Logger)是入口,真正幹活兒的是處理器(Handler),處理器(Handler)還能夠經過過濾器(Filter)和格式器(Formatter)對要輸出的日誌內容作過濾和格式化處操做。

logging日誌模塊相關類及其經常使用方法 下面介紹與logging四大組件相關的類:Logger,Handler,Filter,Formatter。 Logger類,Logger對象有3個任務要作:

  • 嚮應用程序代碼暴露幾個方法,使應用程序能夠在運行時記錄日誌消息;
  • 基於日誌嚴重級別(默認的過濾設置)或Filter對象來決定要對哪些日誌進行後續處理;
  • 將日誌消息傳送給全部感興趣的日誌Handler;

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對象,好比:一個應用程序可能想要實現如下幾個日誌需求:

  • 把全部日誌都發送到一個日誌文件中;
  • 把全部嚴重級別大於等於error的日誌發送到stdout(標準輸出);
  • 把全部嚴重級別爲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,如stdout、stderr或任何file-like對象
logging.FileHandler 將日誌消息發送到磁盤文件,默認狀況下文件大小會無限增加
logging.handlers.RotatingFileHandler 將日誌消息發送到磁盤文件,並支持日誌文件按大小切割
logging.handlers.TimeRotatingFileHandler 將日誌消息發送到磁盤文件,並支持日誌文件按時間切割
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"信息的出現

Formatter類,Formatter對象用於配置日誌信息的最終順序、結構和內容。與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)

好比,一個filer實例化時傳遞的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數量等;

3.logging日誌流處理流程 下圖描述了日誌流的處理流程:

咱們來描述下上面這個圖的日誌流處理流程:

  • (1)在用戶代碼進行日誌記錄函數調用,如:logger.info(...),logging.debug(...)等;
  • (2)判斷要記錄的日誌級別是否知足日誌器設置的級別要求(要記錄的日誌級別要大於等於日誌器設置的級別纔算知足要求),若是不知足則該日誌記錄會被丟棄並終止後續的操做,若是知足則繼續下一步操做;
  • (3)根據日誌記錄函數調用時傳入的參數,建立一個日誌記錄(LogRecord類)對象;
  • (4)判斷日誌記錄器上設置的過濾器是否拒絕這條日誌記錄,若是日誌記錄器上的某個過濾器拒絕,則該日誌記錄會被丟棄並終止後續的操做,若是日誌記錄器上設置的過濾器沒有拒絕這條日誌記錄或者日誌記錄器上沒有設置過濾器,則繼續下一步操做---將日誌記錄分別交給該日誌器上添加的各個處理器;
  • (5)判斷要記錄的日誌級別是否知足處理器設置的級別要求(要記錄的日誌級別要大於等於該處理器設置的日誌級別纔算知足要求),若是不知足,記錄將會被該處理器丟棄並終止後續的操做,若是知足則繼續下一步操做;
  • (6)判斷該處理器上設置的過濾器是否拒絕這條日誌記錄,若是該處理器上的某個過濾器拒絕,則該日誌記錄會被當前處理器丟棄並終止後續的操做,若是當前處理器上設置的過濾器沒有拒絕這條日誌記錄或當前處理器上沒有設置過濾器測繼續下一步操做;
  • (7)若是能到這一步,說明這條日誌記錄通過了層層關卡容許被輸出了,此時當前處理器會根據自身設置的格式器(若是沒有設置則使用默認格式)將這條日誌記錄進行格式化,最後將格式化後的結果輸出到指定位置(文件、網絡、類文件的Stream等);
  • (8)若是日誌器被設置了多個處理器的話,上面的第5-8步會執行屢次;
  • (9)這裏纔是完整流程的最後一步:判斷該日誌器輸出的日誌消息是否須要傳遞給上一級logger(前面提到過,日誌器是有層級關係的)的處理器,若是propagate屬性值爲1則表示日誌消息將會被輸出處處理器指定的位置,同時還會被傳遞給parent日誌器的handlers進行處理,直到當前日誌器的propagate屬性值爲0中止,若是propagate值爲0,則表示不向parent日誌器的handlers傳遞該消息,到此結束。

可見,一條日誌信息要想被最終輸出須要依次通過如下幾回過濾:

  • 日誌器級別過濾;
  • 日誌器的過濾器過濾;
  • 日誌器的處理器級別過濾;
  • 日誌器的處理器的過濾器過濾;

須要說明的是:關於上面第9個步驟,若是propagate值爲1,那麼日誌消息會直接傳遞交給上一級logger的handlers進行處理,此時上一級的logger的日誌級別並不會對該日誌消息等級過濾。

5、logging模塊組件使用

如今,咱們對logging模塊的重要組件及整個日誌流處理流程都有了一個比較全面的瞭解,下面咱們來看一個例子: 1.需求 如今有如下幾個日誌記錄的需求:

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

2.分析

  • 要記錄全部級別的日誌,所以日誌器的有效level須要設置爲最低級別----DEBUG;
  • 日誌須要被髮送到兩個不一樣的目的地,所以須要爲日誌器設置兩個handler;另外,兩個目的地都是磁盤文件,所以這兩個handler都是與FileHandler相關的;
  • all.log要求按照時間進行日誌切割,所以它須要用logging.handlers.TimeRotatingFileHandler;而error.log沒有要求日誌切割,所以可使用FileHandler;
  • 兩個日誌文件格式不一樣,所以須要對這兩個handler分別設置格式器;

3.實現代碼

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文件輸出內容:

2018-05-05 23:23:53,716 - DEBUG - debug message
2018-05-05 23:23:53,717 - INFO - info message
2018-05-05 23:23:53,717 - WARNING - warning message
2018-05-05 23:23:53,717 - ERROR - error message
2018-05-05 23:23:53,717 - CRITICAL - critical message

運行以上代碼,error.log文件輸出內容:

2018-05-05 23:23:53,717 - ERROR - logging module.py[:45] - error message
2018-05-05 23:23:53,717 - CRITICAL - logging module.py[:46] - critical message

6、logging配置方式

做爲開發者,咱們能夠經過如下三種方式來配置logging:

  • 使用Python代碼顯式的建立loggers,handlers和formatters並分別調用它們的配置函數;
  • 建立一個日誌配置文件,而後使用fileConfig()函數來讀取該文件的內容;
  • 建立一個包含配置信息的dict,而後把它傳遞給dictConfig()函數;

具體參考Python配置日誌的幾種方式

7、日誌輸出中添加上下文信息

除了傳遞給日誌記錄函數的參數外,有時候咱們還想在日誌輸出中包含一些額外的上下文信息。好比,在一個網絡應用中,可能但願在日誌中記錄客戶端的特定信息,如:遠程客戶端的IP地址和用戶名,這裏咱們來介紹如下幾種實現方式:

  • 經過向日志記錄函數傳遞一個extra參數引入上下文信息;
  • 使用LoggerAdapters引入上下文信息;
  • 使用Filters引入上下文信息;

具體參考Python日誌輸出中添加上下文信息

參考文獻

相關文章
相關標籤/搜索