logging模塊是Python內置的標準模塊,主要用於輸出運行日誌,能夠設置輸出日誌的等級、日誌保存路徑、日誌文件回滾等;相比print,具有以下優勢:html
import logging
import logging logging.debug('debug message') logging.info('info message') logging.warning('warning message') logging.error('error message') logging.critical('critical message') # 輸出爲: # WARNING:root:warning message # ERROR:root:error message # CRITICAL:root:critical message
從這個例子中,咱們能夠看到默認狀況下python
關於logging的內容遠不止這麼簡單,且慢慢看下去。程序員
在後面分別會說明日誌級別,模塊級函數記錄日誌,logging模塊日誌流處理流程服務器
日誌級別分類網絡
說明多線程
logging模塊中提供了兩種記錄日誌的方式:ide
說明: logging模塊提供的模塊級別的那些函數實際上也是經過這幾個組件的相關實現類來記錄日誌的,只是在建立這些類的實例時設置了一些默認值。函數
上面說到了logging的basicConfig函數進行參數的配置。工具
logging.basicConfig函數各個參數測試
%(asctime)s:日誌事件發生的時間--人類可讀時間,如:2003-07-08 16:49:45,896
%(created)f:日誌事件發生的時間--時間戳,就是當時調用time.time()函數返回的值
%(relativeCreated)d:日誌事件發生的時間相對於logging模塊加載時間的相對毫秒數(目前還不知道幹嗎用的)
%(msecs)d:日誌事件發生事件的毫秒部分
%(levelname)s:該日誌記錄的文字形式的日誌級別('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL')
%(levelno)s:該日誌記錄的數字形式的日誌級別(10, 20, 30, 40, 50)
%(name)s:所使用的日誌器名稱,默認是'root',由於默認使用的是 rootLogger
%(message)s:日誌記錄的文本內容,經過 msg % args計算獲得的
%(pathname)s:調用日誌記錄函數的源碼文件的全路徑
%(filename)s:pathname的文件名部分,包含文件後綴
%(module)s:filename的名稱部分,不包含後綴
%(lineno)d:調用日誌記錄函數的源代碼所在的行號
%(funcName)s:調用日誌記錄函數的函數名
%(process)d:進程ID
%(processName)s:進程名稱,Python 3.1新增
%(thread)d:線程ID
%(thread)s:線程名稱
ValueError
異常;在一開始寫的那個簡單的例子其實就是用模塊級函數寫的日誌。除了上面的那種寫法,還有另外一種寫法。
import logging 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.
正如前面一開始說的那樣,日誌的的默認日誌級別爲WARNING,只有它和它以上的日誌記錄被輸出。而默認的輸出格式是:日誌級別:日誌器名稱:日誌內容。而這樣輸出也是由於
logging模塊提供的日誌記錄函數所使用的日誌器設置的日誌格式默認是BASIC_FORMAT,其值爲:
"%(levelname)s:%(name)s:%(message)s"
詳細的默認值能夠看源碼,當咱們沒有提供任何配置信息的時候,這些函數都會去調用logging.basicConfig(**kwargs)
方法,且不會向該方法傳遞任何參數。繼續查看basicConfig()
方法的代碼就能夠找到上面這些問題的答案了。
import logging 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日誌文件中看到下面的輸出結果 # 08/12/2018 21:54:39 PM - DEBUG - This is a debug log. # 08/12/2018 21:54:39 PM - INFO - This is a info log. # 08/12/2018 21:54:39 PM - WARNING - This is a warning log. # 08/12/2018 21:54:39 PM - ERROR - This is a error log. # 08/12/2018 21:54:39 PM - CRITICAL - This is a critical log.
會上面這些基本的日誌記錄就算是掌握了。
說明:
logging.basicConfig()
函數是一個一次性的簡單配置工具使,也就是說只有在第一次調用該函數時會起做用,後續再次調用該函數時徹底不會產生任何操做的,屢次調用的設置並非累加操做。RootLogger
類的實例,其名稱爲'root',它是處於日誌器層級關係最頂層的日誌器,且該實例是以單例模式存在的。logging.warning('%s is %d years old.', 'Tom', 10)
,輸出內容爲WARNING:root:Tom is 10 years old.
exc_info, stack_info, extra
,下面對這幾個關鍵字參數做個說明。一個例子:
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':'192.168.199.22'}) # 打印結果爲 # 08/12/2018 22:01:43 PM - WARNING - Tom[192.168.199.22] - Some one delete the log file. # NoneType: None # Stack (most recent call last): # File "D:/workspace/modue/bin.py", line 6, in <module> # logging.warning("Some one delete the log file.", exc_info=True, stack_info=True, extra={'user': 'Tom', 'ip':'192.168.199.22'})
在說明用logging模塊的四大組件記錄日誌以前, 頗有必要對logging模塊所包含的重要組件以及其工做流程作個全面、簡要的介紹,這有助於咱們更好的理解咱們所寫的代碼(將會觸發什麼樣的操做)。
logging模塊的四大組件
logging模塊就是經過這些組件來完成日誌處理的,上面所使用的logging模塊級別的函數也是經過這些組件對應的類來實現的。
簡單點說就是:日誌器(logger)是入口,真正幹活兒的是處理器(handler),處理器(handler)還能夠經過過濾器(filter)和格式器(formatter)對要輸出的日誌內容作過濾和格式化等處理操做。
下面介紹下與logging四大組件相關的類:Logger,Handler,Filter,Formatter。
Logger對象有3個任務要作
logger對象最經常使用的方法分爲兩類:配置方法和消息發送。
建立一個Logger對象
logger = logging.Logger
logger = logging.getLogger()
當咱們建立了一個Logger對象,其實它並沒什麼功能,須要咱們給它配置添加。
經常使用的配置方法
(下面寫的logger都是Logger類的實例化對象):
import logging logger = logging.getLogger() # 得到一個Logger實例化對象 fh = logging.FileHandler('my_log') # 建立一個FileHandler對象,用於寫入日誌文件,參數爲日誌寫到哪一個文件中 ch = logging.StreamHandler() # 建立一個StreamHandler對象,用於輸出到控制檯 logger.addHandler(fh) # 爲Logger對象添加FileHandler對象 logger.addHandler(ch) # 爲Logger對象添加StreamHandler對象 logger.removeHandler(fh) # 爲Logger對象刪除FileHandler對象 logger.removeHandler(ch) # 爲Logger對象刪除StreamHandler對象
建立日誌記錄
例如:
logger = logging.getLogger() # 得到一個Logger實例化對象 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')
說明:
關於logger的層級結構與有效等級的說明:
|
Handler對象的做用是(基於日誌消息的level)將消息分發到handler指定的位置(文件、網絡、郵件等)。在上面的Logger類中,咱們使用了Handler類的一些知識。因此對Handler類應該有了必定的瞭解,這裏會詳細的介紹這個類。
Logger對象能夠經過addHandler()方法爲本身添加0個或者更多個handler對象。好比,一個應用程序可能想要實現如下幾個日誌需求:
這種場景就須要3個不一樣的handlers,每一個handler複雜發送一個特定嚴重級別的日誌到一個特定的位置。
經常使用的Handler
handle對象經常使用配置方法
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表示能夠經過過濾。
Formater對象用於配置日誌信息的最終順序、結構和內容。與logging.Handler基類不一樣的是,應用代碼能夠直接實例化Formatter類。另外,若是你的應用程序須要一些特殊的處理行爲,也能夠實現一個Formatter的子類來完成。
Formatter類的構造方法定義以下:
logging.Formatter.__init__(fmt=None, datefmt=None, style='%')
可見,該構造方法接收3個可選參數:
下圖大體表述了日誌流的處理流程
咱們來描述下上面這個圖的日誌流處理流程:
可見,一條日誌信息要想被最終輸出須要依次通過如下幾回過濾:
須要說明的是:
1.關於上面第9個步驟,若是propagate值爲1,那麼日誌消息會直接傳遞交給上一級logger的handlers進行處理,此時上一級logger的日誌等級並不會對該日誌消息進行等級過濾。
2.使用getLogger()方法的時候,若是要返回的日誌器名字相同,返回的爲同一個日誌器,由於它的內部是用單例生成的,因此當多個對象使用同一日誌器而後進行設置時,新的設置會覆蓋舊的設置。而當輸出的時候,有幾個對象要顯示,就顯示幾遍日誌。
3.日誌器是一個樹形結構,好比:getLogger("mylogger.sontree")mylogger是root的孩子,sontree是mylogger的孩子,root的孫子。
4.當咱們使用子層的日誌器的時候,它會默認往上去找父級直到找到root,有幾層顯示幾遍日誌:
例如:
View Codeimport logging logger = logging.getLogger() # 得到一個Logger實例化對象,因爲沒寫參數返回的root日誌器 ch = logging.StreamHandler() # 建立一個StreamHandler對象,用於輸出到控制檯 logger.addHandler(ch) # 爲Logger對象添加StreamHandler對象 logger.setLevel("ERROR") logger1 = logging.getLogger("my_log") # 得到一個名爲Logger實例化對象 logger1.addHandler(ch) # # 爲Logger對象添加StreamHandler對象 logger1.setLevel("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.debug('logger1 debug message') logger1.info('logger1 info message') logger1.warning('logger1 warning message') logger1.error('logger1 error message') logger1.critical('logger1 critical message') # 輸出結果爲: # logger error message # logger critical message # logger1 debug message # logger1 debug message # logger1 info message # logger1 info message # logger1 warning message # logger1 warning message # logger1 error message # logger1 error message # logger1 critical message # logger1 critical message例子中,logger不寫參數獲取的日誌器爲root,它是正常顯示的,而logger1獲取的則是名爲my_log的日誌器,它除了打印本身的,還向上去找,找到了它的父級就是頂級的root,因此也打印了一遍,這樣就看到了後面打印了兩遍的日誌。你能夠再按上面說到建立一個my_log日誌器的子日誌器進行測試。
5.上面提到了會重複打印的問題,那麼我不想這樣怎麼辦。那就是把例子中的logger.addHandler(ch) 註釋了,不讓父日誌器的handler工做就好了。因此咱們須要注意的就是每當一個日誌器的父日誌器有輸出的時候,它就會重複輸出一次。
如今,咱們對logging模塊的重要組件及整個日誌流處理流程都應該有了一個比較全面的瞭解,下面咱們來看一個例子。
如今有如下幾個日誌記錄的需求:
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文件輸出
2018-08-13 11:00:58,246 - DEBUG - debug message
2018-08-13 11:00:58,247 - INFO - info message
2018-08-13 11:00:58,247 - WARNING - warning message
2018-08-13 11:00:58,247 - ERROR - error message
2018-08-13 11:00:58,247 - CRITICAL - critical message
error.log文件輸出
2018-08-13 11:00:58,247 - ERROR - bin.py[:21] - error message
2018-08-13 11:00:58,247 - CRITICAL - bin.py[:22] - critical message
【轉】 向日志輸出中添加上下文信息:http://www.cnblogs.com/yyds/p/6897964.html
參考文檔: