Python學習——logging模組
一、概述
好的程式開發,往往會兼顧到日誌輸出的需求,以便給使用者提供必要的日誌資訊便於確認程式執行狀態、排錯等等。這些日誌一般包括程式的正常執行日誌、訪問日誌、錯誤日誌、資料儲存日誌等型別。在python中logging模組提供了標準的日誌介面,可以滿足我們對日誌輸出的各種需求,下面一一詳述。
二、logging模組入門
2.1 日誌級別
業內常用的日誌有五個級別,分別是:debug,info,warning,error和critical,其中debug級別最低,critical級別最高,級別越低,列印的日誌等級越多。各個級別的定義如下:
Level | When it’s used |
---|---|
DEBUG | Detailed information, typically of interest only when diagnosing problems. |
INFO | Confirmation that things are working as expected. |
WARNING | An indication that something unexpected happened, or indicative of some problem in the near future(e.g. ‘disk spack low’). The software is still working as expected. |
ERROR | Due to a more serious problem, the software has not been able to perform some function. |
CRITICAL | A serious error, indicating that the program itself may be unable continue running. |
預設的級別是warning,只有在warning級別和以上級別的日誌才會輸出,這樣可以避免日誌過多的問題。當然這個預設的級別是可以自定義修改的,下文詳述。
2.2 標準日誌輸出
最簡單的日誌輸出做輸出到stdout,看程式碼:
import logging
logging.debug('[debug]')
logging.info('[info]')
logging.warning('[warning]')
logging.error('[error]')
logging.critical('[critical]')
輸出:
WARNING:root:[warning]
ERROR:root:[error]
CRITICAL:root:[critical]
# 預設的日誌級別是warning,該級別以下的日誌沒有輸出。
2.3 日誌輸出到檔案
日誌輸出到檔案具有持久化儲存的功能,便於隨時回溯檢視。通過basicConfig可以定義日誌輸出的引數。關鍵引數如下:
關鍵字 | 描述 |
---|---|
filename | 建立一個FileHandler,使用指定的檔名,而不是使用StreamHandler。 |
filemode | 如果指明瞭檔名,指明開啟檔案的模式(如果沒有指明filemode,預設為‘a’) |
format | handler使用指明的格式化字串 |
datefmt | 使用指明的日期/時間格式 |
level | 指明根logger的級別 |
stream | 使用指明的流來初始化StreamHandler。該引數與‘filename’不相容,如果兩個都有,‘stream’被忽略。 |
當然了,這個日誌級別是可以靈活自定義的。
import logging
logging.basicConfig(filename='running.log', level=logging.INFO)
logging.debug('[debug]')
logging.info('[info]')
logging.warning('[warning]')
logging.error('[error]')
logging.critical('[critical]')
日誌檔案輸出結果:
2.4 format格式化輸出日誌
前面的示例中,日誌的輸出沒有什麼可讀性而言,這裡可以通過format來增加時間等欄位,提高可讀性。常用的format相關的引數如下:
引數 | 說明 |
---|---|
%(levelno)s | 數字形式的日誌級別 |
%(levelname)s | 文字形式的日誌級別 |
%(pathname)s | 呼叫日誌輸出函式的模組的完整路徑名,可能沒有 |
%(filename)s | 呼叫日誌輸出函式的模組的檔名 |
%(module)s | 呼叫日誌輸出函式的模組名 |
%(funcName)s | 呼叫日誌輸出函式的函式名 |
%(lineno)d | 呼叫日誌輸出函式的語句所在的程式碼行 |
%(created)f | 當前時間,用UNIX標準的表示時間的浮點數表示 |
%(relativeCreated)d | 輸出日誌資訊時的,自Logger建立以來的毫秒數 |
%(asctime)s | 字串形式的當前時間。預設格式是“2018-04-25 10:39:50, 896”。逗號後面的是毫秒 |
%(thread)d | 執行緒ID。可能沒有 |
%(threadName)s | 執行緒名。可能沒有 |
%(process)d | 程式ID。可能沒有 |
%(message)s | 使用者輸出的訊息 |
import logging
logging.basicConfig(filename='running.log', level=logging.INFO, format='%(asctime)s [%(levelname)s] %(module)s %(message)s')
logging.debug('[debug]')
logging.info('service started')
logging.warning('memory leak')
logging.error('can not started')
logging.critical('fatal error')
程式輸出:
2018-04-25 10:54:22,479 [INFO] p_logging service started
2018-04-25 10:54:22,479 [WARNING] p_logging memory leak
2018-04-25 10:54:22,479 [ERROR] p_logging can not started
2018-04-25 10:54:22,479 [CRITICAL] p_logging fatal error
因為示例程式的檔名為p_logging.py,所以這裡日誌輸出的模組名顯示為p_logging。還可以通datefmt引數自定義日期時間輸出格式:
import logging
logging.basicConfig(filename='running.log', level=logging.INFO, format='%(asctime)s [%(levelname)s] %(module)s %(message)s',datefmt='%m/%d/%Y %H:%M:%S')
logging.debug('[debug]')
logging.info('service started')
logging.warning('memory leak')
logging.error('can not started')
logging.critical('fatal error')
輸出:
04/25/2018 10:56:41 [INFO] p_logging service started
04/25/2018 10:56:41 [WARNING] p_logging memory leak
04/25/2018 10:56:41 [ERROR] p_logging can not started
04/25/2018 10:56:41 [CRITICAL] p_logging fatal error
三、logging模組進階
前文講述了日誌輸出的一些基本用法,複雜的用法還需要進階學習:
3.1 logging模組的主要類概述
python使用logging模組記錄日誌涉及四個主要類:
logger:記錄器,提供了應用程式可以直接使用的介面。
handler:處理器,將(logger建立的)日誌記錄傳送到合適的目的輸出。必須有handler才能捕獲輸出的日誌。
filter:過濾器,對日誌進行過濾來決定輸出哪條日誌記錄。
formatter:格式化器,決定日誌記錄的最終輸出格式。
3.2 logger詳解
Logger是一個樹形層級結構,在使用介面debug,info,warn,error,critical之前必須建立Logger例項,即建立一個記錄器,如果沒有顯式的進行建立,則預設建立一個root logger,並應用預設的日誌級別(WARN),處理器Handler(StreamHandler,即將日誌資訊列印輸出在標準輸出上),和格式化器Formatter(預設的格式即為第一個簡單使用程式中輸出的格式)。
建立方法: logger = logging.getLogger(logger_name)
建立Logger例項後,可以使用以下方法進行相關設定:
logger.setLevel(logging.ERROR) # 設定日誌級別為ERROR,即只有日誌級別大於等於ERROR的日誌才會輸出;
logger.addHandler(handler_name) # 為Logger例項增加一個處理器;
logger.removeHandler(handler_name) # 為Logger例項刪除一個處理器;
logger.addFilter(filt) #為logger例項增加過濾器;
logger.removeFilter(filt) #為logger例項刪除過濾器;
logger.debug()、logger.info()、logger.warning()、logger.error()、logger.critical() #設定指定級別的日誌輸出;
len(logger.handlers) #獲取handler的個數。
3.3 handler詳解
handler物件負責傳送相關的資訊到指定目的地。Python的日誌系統有多種Handler可以使用。有些Handler可以把資訊輸出到控制檯,有些Logger可以把資訊輸出到檔案,還有些 Handler可以把資訊傳送到網路上。如果覺得不夠用,還可以編寫自己的Handler。可以通過addHandler()方法新增多個多handler 。
Handler處理器型別有很多種,比較常用的有三個,StreamHandler,FileHandler,NullHandler,詳情可以訪問Python logging.handlers
建立StreamHandler之後,可以通過使用以下方法設定日誌級別,設定格式化器Formatter,增加或刪除過濾器Filter。
- ch.setLevel(logging.WARN) # 指定日誌級別,低於WARN級別的日誌將被忽略;
- ch.setFormatter(formatter_name) # 設定一個格式化器formatter;
- ch.addFilter(filter_name) # 增加一個過濾器,可以增加多個;
- ch.removeFilter(filter_name) # 刪除一個過濾器;
StreamHandler
建立方法: sh = logging.StreamHandler(stream=None)
FileHandler
建立方法: fh = logging.FileHandler(filename, mode='a', encoding=None, delay=False)
NullHandler
NullHandler類位於核心logging包,不做任何的格式化或者輸出。
本質上它是個“什麼都不做”的handler,由庫開發者使用。
下面講解handler的重要知識點:
- logging.StreamHandler
說明:建立一個handler物件,使用這個Handler可以向類似與sys.stdout或者sys.stderr的任何檔案物件(file object)輸出資訊,也就是螢幕輸出。
它的建構函式是:StreamHandler([strm]),其中strm引數是一個檔案物件,預設是sys.stderr。
import logging
logger = logging.getLogger('testlog') # 建立一個logger物件
logger.setLevel(logging.DEBUG) # 設定logger的日誌級別
stdout = logging.StreamHandler() # 建立一個handler物件,日誌輸出到stdout
stdout.setLevel(logging.INFO) # 設定handler的日誌級別
logger.addHandler(stdout) # 為logger增加指定的handler
logger.debug('debug log') # 輸出日誌
logger.info('info log') # 輸出日誌
輸出:
info log
上述示例程式中,由於handler中設定的日誌級別是info,比logger中的debug還要高,因此結果輸出中僅僅輸出了info級別的日誌。
- logging.FileHandler
說明:和StreamHandler類似,用於向一個檔案輸出日誌資訊,不過FileHandler會幫你開啟這個檔案。
它的建構函式是:FileHandler(filename[,mode])。filename是檔名,必須指定一個檔名。mode是檔案的開啟方式。參見Python內建函式open()的用法。預設是’a’,即新增到檔案末尾。
import logging
logger = logging.getLogger('testlog')
logger.setLevel(logging.DEBUG)
fh = logging.FileHandler('test.log') #建立一個fileHandler物件,預設追加寫
fh.setLevel(logging.INFO) #設定handler的日誌級別
formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(message)s --%(module)s')
fh.setFormatter(formatter) #為handler設定格式化器
logger.addHandler(fh)
logger.debug('debug log')
logger.info('info log')
logger.error('error log')
輸出test.log的內容:
2018-04-25 11:53:28,231 [INFO] info log --p_logging
2018-04-25 11:53:28,232 [ERROR] error log --p_logging
- logging.handlers.RotatingFileHandler
說明:這個Handler類似於上面的FileHandler,但是它可以管理檔案大小。當檔案達到一定大小(位元組數)之後,它會自動進行日誌檔案滾動切割:將當前日誌檔案改名,然後建立 一個新的同名日誌檔案繼續輸出。比如日誌檔案是chat.log。當chat.log達到指定的大小之後,RotatingFileHandler自動把 檔案改名為chat.log.1。不過,如果chat.log.1已經存在,會先把chat.log.1重新命名為chat.log.2。。。最後重新建立 chat.log,繼續輸出日誌資訊。
它的建構函式是:RotatingFileHandler( filename, mode, maxBytes, backupCount),其中filename和mode兩個引數和FileHandler一樣。
maxBytes用於指定日誌檔案的最大檔案大小。如果maxBytes為0,意味著日誌檔案可以無限大,這時上面描述的重新命名過程就不會發生。
backupCount用於指定保留的備份檔案的個數。比如,如果指定為2,當上面描述的重新命名過程發生時,原有的chat.log.2並不會被更名,而是被刪除。
import logging
from logging import handlers
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
#單個日誌檔案最大50位元組,最多保留3個檔案
fh = logging.handlers.RotatingFileHandler(filename='time.log', maxBytes=50, backupCount=3)
fh.setLevel(logging.INFO)
#設定formatter
formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(message)s --%(module)s')
fh.setFormatter(formatter)
logger.addHandler(fh)
logger.debug('debug log')
logger.info('info log')
logger.error('error log')
輸出:
time.log.1 檔案 2018-04-25 11:08:26,253 [INFO] info log --log
time.log 檔案 2018-04-25 11:08:26,254 [ERROR] error log --log
上述示例程式中由於一行日誌的輸出剛好達到50位元組的日誌檔案切割臨界值,所以兩行log輸出到了兩個日誌檔案中,且越早輸出的日誌會寫到了字尾越大的日誌檔案中(最新的日誌檔案,可認為字尾為0)
logging.handlers.TimedRotatingFileHandler
說明:這個Handler和RotatingFileHandler類似,不過,它沒有通過判斷檔案大小來決定何時重新建立日誌檔案,而是間隔一定時間就自動建立新的日誌檔案。重新命名的過程與RotatingFileHandler類似,不過新的檔案不是附加數字,而是當前時間。
它的建構函式是:TimedRotatingFileHandler( filename,when,interval,backupCount),其中filename引數和backupCount引數和RotatingFileHandler具有相同的意義。interval是時間間隔。
when引數是一個字串。表示時間間隔的單位,不區分大小寫。它有以下取值:①S:秒②M:分③H:小時④D:天⑤W :每星期(interval==0時代表星期一)⑥midnight:每天凌晨
import logging
from logging import handlers
import time
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
#每隔5秒切割一次日誌檔案,最多保留3份
fh = logging.handlers.TimedRotatingFileHandler(filename='time.log', when='S', interval=3, backupCount=3)
fh.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(message)s --%(module)s')
fh.setFormatter(formatter)
logger.addHandler(fh)
logger.debug('debug log')
time.sleep(3)
logger.info('info log')
time.sleep(3)
logger.error('error log')
輸出:
time.log 2018-02-21 11:18:05,867 [ERROR] error log --log
time.log.2018-02-21_11-18-02 2018-02-21 11:18:02,842 [INFO] info log --log
time.log.2018-02-21_11-17-59 檔案為空,但因為有sleep導致達到日誌滾動的時間閾值,所以滾動生成了一個空日誌檔案
3.4 Formatter 格式化器
使用Formatter物件設定日誌資訊最後的規則、結構和內容,預設的時間格式為%Y-%m-%d %H:%M:%S。
建立方法: formatter = logging.Formatter(fmt=None, datefmt=None)
其中,fmt是訊息的格式化字串,datefmt是日期字串。如果不指明fmt,將使用’%(message)s’。如果不指明datefmt,將使用ISO8601日期格式。
3.5 filter 過濾器
Handlers和Loggers可以使用Filters來完成比級別更復雜的過濾。Filter基類只允許特定Logger層次以下的事件。例如用‘A.B’初始化的Filter允許Logger ‘A.B’, ‘A.B.C’, ‘A.B.C.D’, ‘A.B.D’等記錄的事件,logger‘A.BB’, ‘B.A.B’ 等就不行。 如果用空字串來初始化,所有的事件都接受。
建立方法: filter = logging.Filter(name='')
filter的示例程式待後續補充更新。
四、綜合運用–把日誌同時輸出到標準輸出和日誌檔案
思路比較簡單了,先後建立StreamHandler和FileHandler,定義各自的level、formatter,最後add到logger即可。
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
formater = logging.Formatter('%(asctime)s [%(levelname)s] %(name)s %(message)s ---%(module)s')
sh = logging.StreamHandler()
sh.setLevel(logging.WARN)
sh.setFormatter(formater)
fh = logging.FileHandler('test.log')
fh.setLevel(logging.WARN)
fh.setFormatter(formater)
logger.addHandler(sh)
logger.addHandler(fh)
logger.debug('debug log')
logger.info('info log')
logger.error('error log')
輸出:
2018-02-21 11:39:38,398 [ERROR] __main__ error log ---log
五、概念總結
以下是相關概念總結:
熟悉了這些概念之後,有另外一個比較重要的事情必須清楚,即Logger是一個樹形層級結構;
Logger可以包含一個或多個Handler和Filter,即Logger與Handler或Fitler是一對多的關係;
一個Logger例項可以新增多個Handler,一個Handler可以新增多個格式化器或多個過濾器,而且日誌級別將會繼承,但Handler中的日誌級別會覆蓋Logger中的日誌級別設定
參考:http://www.cnblogs.com/linupython/p/8456431.html
相關文章
- 模組學習之logging模組
- python的logging模組Python
- Python logging模組的使用Python
- python logging模組使用總結Python
- python常用模組補充hashlib configparser logging,subprocess模組Python
- [Python模組學習] glob模組Python
- hashlib、logging模組
- python之logging日誌模組詳解Python
- Python:使用logging模組記錄日誌Python
- Python之logging模組相關介紹Python
- Python強大的日誌模組loggingPython
- Python學習——shelve模組Python
- Python學習——xml模組PythonXML
- Python學習——configparser模組Python
- Python學習——hashlib模組Python
- Python學習之模組Python
- 【python介面自動化】- logging日誌模組Python
- python logging模組註冊流程(以logging.config.dictConfig流程為例)Python
- 學習 python logging(1): 基本用法Python
- Python學習之常用模組Python
- 學習Python的urllib模組Python
- Python學習之 datetime模組Python
- logging模組配置筆記筆記
- python基礎學習16—-模組Python
- Python學習之模組與包Python
- 日誌記錄模組logging
- python常識系列08-->logging模組基礎入門Python
- Python–logging模組不同級別寫入到不同檔案Python
- Python的shutil zipfile tarfile模組學習Python
- Python學習之如何引用Python自定義模組?Python
- 模組學習之hashlib模組
- Python學習【第十四篇】shutil模組Python
- 『無為則無心』Python日誌 — 64、Python日誌模組logging介紹Python
- .Net Core Logging模組原始碼閱讀原始碼
- itertools 模組學習
- python開發學習之如何更好的引用Python模組?Python
- Python開發常用的庫及模組!Python學習教程Python
- Python學習筆記_函式_匯入模組Python筆記函式