2.1簡介
????????Logging庫采用模塊化方法并且提供了幾類組件,它們分別是loggers, handlers, filters, and formatters。
Loggers組件暴露出應用代碼能夠直接調(diào)用的接口。
Handlers組件把日志記錄(Logger組件創(chuàng)建的)發(fā)給合適的目標。
Filter組件提供一個更優(yōu)越的方式來決定輸出哪個日志記錄。
-
Formatters組件指定最終輸出的日志記錄的格式。
????????在一個LogRecord
實例中缠导,事件日志信息會在loggers,handlers溉痢,filters和formatters四個組件中進行傳遞僻造。
????????通過調(diào)用Logger類實例的方法來執(zhí)行日志記錄(后面統(tǒng)稱為loggers)。每個實例都有一個名稱孩饼,并且它們在概念上以.
作為分隔符髓削,排在命名空間層次結構之后。例如镀娶,一個名為‘scan’日志記錄器是‘scan.text’立膛,‘scan.html’和‘scan.pdf’記錄器的父集。Logger的名稱可以取你想要的,建議最好是能表明該記錄器所記錄的區(qū)域宝泵。
????????當命名日志器時有一個慣例好啰,就是去使用模塊級別的日志器(根據(jù)模塊命名)。每一個要記錄日志的模塊都可以按如下進行命名:logger = logging.getLogger(__name__)
????????這意味著logger的名稱是根據(jù)所追蹤的包/模塊進行命名的儿奶。這也能夠在查詢?nèi)罩拘畔r很方便的知道是哪個包/模塊所記錄的日志信息框往。
????????頂級層次的日志記錄器被稱為root logger
。這些logger所使用的方法與root logger
所調(diào)用的方法有著一樣的名字闯捎,都叫debug()
椰弊, info()
, warning()
隙券, error()
和critical()
。這些功能和方法都具有相同的簽名闹司。root logger
的名稱在輸出信息中是“root”娱仔。
????????當然,如果想將日志信息輸出到不同的位置游桩,這也很方便牲迫。軟件包中包含支持用于將日志消息寫入文件,HTTP GET / POST位置借卧,通過SMTP發(fā)送電子郵件盹憎,通用套接字,隊列或特定于操作系統(tǒng)的日志記錄機制(如syslog或Windows NT事件日志)铐刘。目標位置是由handler類來處理的陪每。如果你想一些特殊的需求,內(nèi)置的handler類也沒有提供镰吵,那么這時需要你自己創(chuàng)建handler類來處理檩禾。
????????默認情況下是不對任何日志消息設置目標位置的。你可以像基礎教程中的那樣疤祭,通過basicConfig()
來設置目標位置(例如控制臺或文件)盼产。當你調(diào)用debug()
,info()
勺馆,warning()
戏售,error()
和critical()
這些方法時,它們會檢測目標位置草穆。如果沒有設置灌灾,在委托給根記錄器來進行實際的消息輸出之前,它們會默認輸出到控制臺(sys.stderr)并且以默認的格式格式化輸出信息悲柱。
????????basicConfig()
默認的輸出信息格式如下:
severity:logger name:message
????????你可以通過改變basicConfig()
的format參數(shù)值來調(diào)整格式紧卒。有關如何構造格式字符串的所有選項,詳情見 Formatter Objects诗祸。
2.2日志記錄流程
????????在記錄事件信息時跑芳,loggers與handlers的處理流程如下圖所示:
2.2.1.Loggers
????????Logger對象有3個作用轴总。1.Logger對象對外提供了幾個方法,這樣應用程序能夠在運行時記錄消息博个。2.Logger對象根據(jù)消息的嚴重性(默認的過濾工具)或過濾對象來確定要處理的日志對象怀樟。3.Logger對象將相關的日志信息傳遞給所有感興趣的日志處理器。
????????Logger對象使用的最廣泛的方法分為兩類:配置和發(fā)送信息盆佣。
????????以下是最常見的用于配置的方法:
-
Logger.setLevel()指定將要處理的最低嚴重性的日志消息往堡,注意
debug
是嚴重級別最低的而critical
是嚴重級別最高的。舉個例子共耍,如果嚴重級別設置為INFO
虑灰,logger對象只會處理INFO
,WARNING
,ERROR
和CRITICAL
消息并且會忽略DEBUG
消息。 - Logger.addHandler() 和Logger.removeHandler()從logger對象中添加和移除處理對象痹兜。
-
Logger.addFilter()和Logger.removeFilter()從logger對象中添加和移除過濾對象穆咐。
????????你不需要每次創(chuàng)建一個Logger對象都調(diào)用這些方法。
????????一旦logger對象配置完成字旭,以下方法就能創(chuàng)建出日志消息: -
Logger.debug(), Logger.info(), Logger.warning(), Logger.error()和Logger.critical()所有這些方法都將創(chuàng)建日志信息以及對應其名字的嚴重等級对湃。這信息實際上是一個格式化的字符串,這字符串包含了標準的字符串替換語法如
%s
,%d
,%f
等等遗淳。這剩下的參數(shù)是一個對象列表用于替換日志信息中的占位符拍柒。關于參數(shù)**kwargs
,日志方法只關心exc_info
關鍵字的值屈暗,根據(jù)這個關鍵字來決定是否記錄異常信息拆讯。 - Logger.exception()創(chuàng)建一個類似于Logger.error()方法的日志消息。這兩者之間的區(qū)別是Logger.exception()會有一個堆棧信息养叛。這個通常是由一個異常處理器進行調(diào)用往果。
-
Logger.log()將日志級別作為一個顯示參數(shù)。這個方法相比于上面的日志方法要相對冗余一铅,但是可以自定義設置日志級別陕贮。
????????getLogger()方法返回一個logger類實例的引用。通過name參數(shù)來指定該logger實例的名稱潘飘,如果沒有指定則命名為root
肮之。名字是以句點分隔的層次結構。當多次調(diào)用getLogger()
方法指定的名稱一樣時卜录,就會返回同一個對象的引用戈擒。在分層列表中較低的logger是列表中較高的logger的子項,舉個例子艰毒,有一個名為foo
的logger筐高,那么名為foo.bar
,foo.bar.baz
和foo.bam
的logger都是foo
logger的子類。
????????logger類有一個等效級別的概念柑土。如果logger沒有顯示設置級別蜀肘,那么logger會沿用它父級的有效級別。如果父級沒找到稽屏,會在父級的父級中找扮宠,以此類推,直到找到一個有效的級別狐榔。根logger的有效級別為WARNING
坛增。當執(zhí)行一個事件時,有效級別就是用于決定需不需要把事件傳遞給logger的處理器薄腻。
????????子logger會將消息傳播給父級相關的處理器收捣。由于這個特性,你不必為一個應用中的所有l(wèi)ogger進行定義和配置庵楷。只要你為頂級的logger配置處理程序并根據(jù)需要創(chuàng)建子logger就足夠了罢艾。
2.2.2.Handlers
????????Handler對象的作用是將合適的日志信息(基于日志信息的優(yōu)先級)轉發(fā)到handler指定位置。Logger對象能夠通過addHandler()方法來添加0個或多個handler對象嫁乘。有這樣一個示例場景昆婿,一個應用球碉,需要將所有的日志信息保存到日志文件中蜓斧,error級別的日志或更高級別的輸出到控制臺,critical級別的日志需要使用郵件提醒睁冬。這個場景需要三個不同的處理器挎春,每個處理器都負責將不同級別的日志信息發(fā)送到指定的位置。
????????標準庫中包含了許多處理器的類型(詳見Useful Handlers)豆拨。在這個教程中主要使用StreamHandler和FileHandler兩個處理器直奋。
????????handler類提供了一些方法供開發(fā)者使用。如果開發(fā)者準備使用內(nèi)置的處理器對象施禾,那么以下就是這些配置方法:
- setLevel()用于指定處理器處理的最低級別脚线。logger對象中也有一個setLevel()方法,logger中的是指定哪些信息轉發(fā)給handle弥搞,而這里的指定哪些信息發(fā)送到指定位置邮绿。
- setFormatter()選擇一個formatter對象供handler使用。
- addFilter()和removeFilter()分別是配置filter和移除filter對象攀例。
????????應用程序代碼不應直接實例化和使用Handler類船逮。相反,Handler類是一個基類粤铭,它定義了所有處理程序應具有的接口挖胃,并建立了子類可以使用的一些默認行為。
2.2.3.Formatters
????????Formatter對象將會配置日志消息的最終順序,結構和內(nèi)容酱鸭。與logging.Handler基類不同吗垮,應用程序代碼可以直接實例化formatter類,當然你有一些特殊的要求或行為凛辣,你可以編寫formatter類的子類抱既。formatter類的構造函數(shù)需要三個可選的參數(shù)——信息格式字符串,日期格式字符串和樣式字符串扁誓。
????????????????logging.Formatter.init(fmt=None, datefmt=None, style='%')
????????如果沒有設置信息格式字符串防泵,那么會使用默認的信息格式。如果沒有設置日期格式字符串蝗敢,默認的日期格式如下:
%Y-%m-%d %H:%M:%S
????????最后會加上毫秒數(shù)捷泞。style
參數(shù)是%, ‘{’或‘$’中的一個。如果沒有指定其中的一個寿谴,那么默認將會使用‘%’锁右。如果style
參數(shù)的值為‘%’,消息格式字符串就會使用%(<dictionary key>)s
樣式的字符串進行替換讶泰。這些可能的鍵在LogRecord attributes羅列出來了咏瑟。如果style
的值為‘{’,那么消息字符串的格式與str.format()
保持一致痪署。如果style
的值為‘$’码泞,那么消息格式字符串應該與string.Template.substitute()的格式保持一致。
????????接下來的信息格式字符串的含義是以可讀的時間格式狼犯,信息的嚴重級別和信息內(nèi)容的順序進行日志記錄:
'%(asctime)s - %(levelname)s - %(message)s'
2.2.4.Configuring Logging
程序配置logging有以下3種方式:
- 明確使用python代碼創(chuàng)建loggers對象, handlers對象,和formatters對象并調(diào)用上面列出的配置方法進行配置余寥。
- 創(chuàng)建一個日志配置文件并使用fileConfig()方法來讀取它。
- 創(chuàng)建一個含有配置信息的字典悯森,再將該字典傳遞給dictConfig()方法宋舷。
????????有關最后兩個選項的參考文檔,詳見Configuration functions瓢姻。接下來的例子是使用python代碼簡單配置一個logger祝蝠,一個控制臺處理器和一個簡單的格式化類:
import logging
# create logger
logger = logging.getLogger('simple_example')
logger.setLevel(logging.DEBUG)
# create console handler and set level to debug
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
# create formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# add formatter to ch
ch.setFormatter(formatter)
# add ch to logger
logger.addHandler(ch)
# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warning('warning message')
logger.error('error message')
logger.critical('critical message')
????????執(zhí)行代碼,顯示如下:
$ python3 simple.py
2018-08-29 10:29:47,835 - simple_example - DEBUG - debug message
2018-08-29 10:29:47,835 - simple_example - INFO - info message
2018-08-29 10:29:47,836 - simple_example - WARNING - warning message
2018-08-29 10:29:47,836 - simple_example - ERROR - error message
2018-08-29 10:29:47,836 - simple_example - CRITICAL - critical message
????????以下的例子使用配置文件進行配置:
import logging
import logging.config
logging.config.fileConfig('logging.conf')
# create logger
logger = logging.getLogger('simpleExample')
# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')
????????以下是logging.conf文件:
[loggers]
keys=root,simpleExample[handlers]
keys=consoleHandler[formatters]
keys=simpleFormatter[logger_root]
level=DEBUG
handlers=consoleHandler[logger_simpleExample]
level=DEBUG
handlers=consoleHandler
qualname=simpleExample
propagate=0[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=
????????輸出如下:
$ python3 simple_file.py
2018-08-29 11:09:08,217 - simpleExample - DEBUG - debug message
2018-08-29 11:09:08,217 - simpleExample - INFO - info message
2018-08-29 11:09:08,218 - simpleExample - WARNING - warning message
2018-08-29 11:09:08,218 - simpleExample - ERROR - error message
2018-08-29 11:09:08,218 - simpleExample - CRITICAL - critical message