1. logging日志框架
主要包括四部分:
- Loggers: 可供程序直接調用的接口,app通過調用提供的api來記錄日志
- Handlers: 決定將日志記錄分配至正確的目的地
- Filters:對日志信息進行過濾, 提供更細粒度的日志是否輸出的判斷
- Formatters: 制定最終記錄打印的格式布局
1)loggers
loggers 就是程序可以直接調用的一個日志接口,可以直接向logger寫入日志信息。logger並不是直接實例化使用的,而是通過logging.getLogger(name)來獲取對象,事實上logger對象是單例模式,logging是多線程安全的,也就是無論程序中哪里需要打日志獲取到的logger對象都是同一個。但是不幸的是logger並不支持多進程,這個在后面的章節再解釋,並給出一些解決方案。
【注意】loggers對象是有父子關系的,當沒有父logger對象時它的父對象是root,當擁有父對象時父子關系會被修正。舉個例子,logging.getLogger("abc.xyz") 會創建兩個logger對象,一個是abc父對象,一個是xyz子對象,同時abc沒有父對象,所以它的父對象是root。但是實際上abc是一個占位對象(虛的日志對象),可以沒有handler來處理日志。但是root不是占位對象,如果某一個日志對象打日志時,它的父對象會同時收到日志,所以有些使用者發現創建了一個logger對象時會打兩遍日志,就是因為他創建的logger打了一遍日志,同時root對象也打了一遍日志。
上述幾個例子中我們了解到了logging.debug()、logging.info()、logging.warning()、logging.error()、logging.critical()(分別用以記錄不同級別的日志信息),logging.basicConfig()(用默認日志格式(Formatter)為日志系統建立一個默認的流處理器(StreamHandler),設置基礎配置(如日志級別等)並加到root logger(根Logger)中)這幾個logging模塊級別的函數,另外還有一個模塊級別的函數是logging.getLogger([name])(返回一個logger對象,如果沒有指定名字將返回root logger)
Logger是一個樹形層級結構,輸出信息之前都要獲得一個Logger(如果沒有顯示的獲取則自動創建並使用root Logger)。
logger = logging.getLogger()返回一個默認的Logger也即root Logger,並應用默認的日志級別、Handler和Formatter設置。
當然也可以通過Logger.setLevel(lel)指定最低的日志級別,可用的日志級別有logging.DEBUG、logging.INFO、logging.WARNING、logging.ERROR、logging.CRITICAL。
Logger.debug()、Logger.info()、Logger.warning()、Logger.error()、Logger.critical()輸出不同級別的日志,只有日志等級大於或等於設置的日志級別的日志才會被輸出。
logging.getLogger([name]),創建Logger對象。日志記錄的工作主要由Logger對象來完成。在調用getLogger時要提供Logger的名稱(注:多次使用相同名稱來調用getLogger,返回的是同一個對象的引用。),Logger實例之間有層次關系,這些關系通過Logger名稱來體現,如:
p = logging.getLogger(“root”)
c1 = logging.getLogger(“root.c1”)
c2 = logging.getLogger(“root.c2”)
例子中,p是父logger, c1,c2分別是p的子logger。c1, c2將繼承p的設置。如果省略了name參數, getLogger將返回日志對象層次關系中的根Logger。
<1>我們明明通過logger1.setLevel(logging.DEBUG)將logger1的日志級別設置為了DEBUG,為何顯示的時候沒有顯示出DEBUG級別的日志信息,而是從INFO級別的日志開始顯示呢?
原來只要logging.getLogger(name)中名稱參數name相同則返回的Logger實例就是同一個,且僅有一個,也即name與Logger實例一一對應。在logger2實例中通過logger2.setLevel(logging.INFO)設置mylogger的日志級別為logging.INFO,所以最后logger1的輸出遵從了后來設置的日志級別。
-----------------------返回結果只有4條-------------------------
<2>為什么logger1、logger2對應的每個輸出分別顯示兩次?
這是因為我們通過logger = logging.getLogger()顯示的創建了root Logger,而logger1 = logging.getLogger('mylogger')創建了root Logger的孩子(root.)mylogger,logger2同樣。而孩子,孫子,重孫……既會將消息分發給他的handler進行處理也會傳遞給所有的祖先Logger處理,故打印2次,如果 是孫子則打印3次。
---------------返回9條信息-------------
孩子,孫子,重孫……可逐層繼承來自祖先的日志級別、Handler、Filter設置,也可以通過Logger.setLevel(lel)、Logger.addHandler(hdlr)、Logger.removeHandler(hdlr)、Logger.addFilter(filt)、Logger.removeFilter(filt)。設置自己特別的日志級別、Handler、Filter。若不設置則使用繼承來的值。
例2:
dad=logging.getLogger() #創建父對象
son=logging.getLogger('son') #創建子對象
grandson=logging.getLogger('son.grandson') #創建孫對象
fm=logging.Formatter('%(asctime)s--%(name)s--%(message)s') #設置格式
ch=logging.StreamHandler() #設置輸出流向
ch.setFormatter(fm) #添加格式
dad.addHandler(ch) #添加流向
son.addHandler(ch)
grandson.addHandler(ch)
dad.setLevel(logging.ERROR)
son.setLevel(logging.WARNING)
grandson.setLevel(logging.DEBUG)
dad.debug('dad debug message')
dad.info('dad info message')
dad.warning('dad warning message')
dad.error('dad error message')
dad.critical('dad critical message')
son.debug('son debug message')
son.info('son info message')
son.warning('son warning message')
son.error('son error message')
son.critical('son critical message')
grandson.debug('grandson debug message')
grandson.info('grandson info message')
grandson.warning('grandson warning message')
grandson.error('grandson error message')
grandson.critical('grandson critical message')
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2019-01-11 11:22:52,253--root--dad error message
2019-01-11 11:22:52,253--root--dad critical message
2019-01-11 11:22:52,253--son--son warning message
2019-01-11 11:22:52,253--son--son warning message
2019-01-11 11:22:52,253--son--son error message
2019-01-11 11:22:52,253--son--son error message
2019-01-11 11:22:52,253--son--son critical message
2019-01-11 11:22:52,253--son--son critical message
2019-01-11 11:22:52,253--son.grandson--grandson debug message
2019-01-11 11:22:52,253--son.grandson--grandson debug message
2019-01-11 11:22:52,253--son.grandson--grandson debug message
2019-01-11 11:22:52,254--son.grandson--grandson info message
2019-01-11 11:22:52,254--son.grandson--grandson info message
2019-01-11 11:22:52,254--son.grandson--grandson info message
2019-01-11 11:22:52,254--son.grandson--grandson warning message
2019-01-11 11:22:52,254--son.grandson--grandson warning message
2019-01-11 11:22:52,254--son.grandson--grandson warning message
2019-01-11 11:22:52,254--son.grandson--grandson error message
2019-01-11 11:22:52,254--son.grandson--grandson error message
2019-01-11 11:22:52,254--son.grandson--grandson error message
2019-01-11 11:22:52,254--son.grandson--grandson critical message
2019-01-11 11:22:52,254--son.grandson--grandson critical message
2019-01-11 11:22:52,254--son.grandson--grandson critical message
2)Handlers
Handlers 將logger發過來的信息進行准確地分配,送往正確的地方。舉個栗子,送往控制台或者文件或者both或者其他地方(進程管道之類的)。它決定了每個日志的行為,是之后需要配置的重點區域。
每個Handler同樣有一個日志級別,一個logger可以擁有多個handler也就是說logger可以根據不同的日志級別將日志傳遞給不同的handler。當然也可以相同的級別傳遞給多個handlers這就根據需求來靈活的設置了。
3)Filters
Filters 提供了更細粒度的判斷,來決定日志是否需要打印。原則上handler獲得一個日志就必定會根據級別被統一處理,但是如果handler擁有一個Filter可以對日志進行額外的處理和判斷。例如Filter能夠對來自特定源的日志進行攔截or修改甚至修改其日志級別(修改后再進行級別判斷)。
logger和handler都可以安裝filter甚至可以安裝多個filter串聯起來。
限制只有滿足過濾規則的日志才會輸出。
比如我們定義了filter = logging.Filter('a.b.c'),並將這個Filter添加到了一個Handler上,則使用該Handler的Logger中只有名字帶 a.b.c前綴的Logger才能輸出其日志。
filter = logging.Filter('mylogger')
logger.addFilter(filter)
這是只對logger這個對象進行篩選
如果想對所有的對象進行篩選,則:
filter = logging.Filter('mylogger')
fh.addFilter(filter)
ch.addFilter(filter)
這樣,所有添加fh或者ch的logger對象都會進行篩選。

import logging logger = logging.getLogger() # 創建一個handler,用於寫入日志文件 fh = logging.FileHandler('test.log') # 再創建一個handler,用於輸出到控制台 ch = logging.StreamHandler() #設置日志打印格式 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') #fh ch還有添加打印格式 fh.setFormatter(formatter) ch.setFormatter(formatter) # 定義一個filter filter = logging.Filter('mylogger') #添加filter樣式 fh.addFilter(filter) ch.addFilter(filter) logger.addFilter(filter) logger.addHandler(fh) logger.addHandler(ch) logger.setLevel(logging.DEBUG) logger.debug('logger debug message') logger.info('logger info message') logger.warning('logger warning message') logger.error('logger error message') logger.critical('logger critical message') ################################################## logger1 = logging.getLogger('mylogger') logger1.setLevel(logging.DEBUG) logger2 = logging.getLogger('mylogger') logger2.setLevel(logging.INFO) logger1.addHandler(fh) logger1.addHandler(ch) logger2.addHandler(fh) logger2.addHandler(ch) logger1.debug('logger1 debug message') logger1.info('logger1 info message') logger1.warning('logger1 warning message') logger1.error('logger1 error message') logger1.critical('logger1 critical message') logger2.debug('logger2 debug message') logger2.info('logger2 info message') logger2.warning('logger2 warning message') logger2.error('logger2 error message') logger2.critical('logger2 critical message')

2019-01-11 15:44:35,357 - mylogger - INFO - logger1 info message 2019-01-11 15:44:35,357 - mylogger - INFO - logger1 info message 2019-01-11 15:44:35,357 - mylogger - WARNING - logger1 warning message 2019-01-11 15:44:35,357 - mylogger - WARNING - logger1 warning message 2019-01-11 15:44:35,357 - mylogger - ERROR - logger1 error message 2019-01-11 15:44:35,357 - mylogger - ERROR - logger1 error message 2019-01-11 15:44:35,357 - mylogger - CRITICAL - logger1 critical message 2019-01-11 15:44:35,357 - mylogger - CRITICAL - logger1 critical message 2019-01-11 15:44:35,357 - mylogger - INFO - logger2 info message 2019-01-11 15:44:35,357 - mylogger - INFO - logger2 info message 2019-01-11 15:44:35,357 - mylogger - WARNING - logger2 warning message 2019-01-11 15:44:35,357 - mylogger - WARNING - logger2 warning message 2019-01-11 15:44:35,357 - mylogger - ERROR - logger2 error message 2019-01-11 15:44:35,357 - mylogger - ERROR - logger2 error message 2019-01-11 15:44:35,357 - mylogger - CRITICAL - logger2 critical message 2019-01-11 15:44:35,357 - mylogger - CRITICAL - logger2 critical message
4) Formatters
Formatters 指定了最終某條記錄打印的格式布局。Formatter會將傳遞來的信息拼接成一條具體的字符串,默認情況下Format只會將信息%(message)s直接打印出來。Format中有一些自帶的LogRecord屬性可以使用,如下表格:
一個Handler只能擁有一個Formatter 因此如果要實現多種格式的輸出只能用多個Handler來實現。
上圖只是一部分,更詳細的在docs.python.org里找logging模塊