標籤:路徑 改名 out midnight 相同 card 網路 logger 最大
很多程式員都有記錄日誌的需求,並且日誌中包含的資訊即有正常的程式訪問日誌,還可能有錯誤,警告等資訊輸出,python的logging模組提供了標準的日誌介面,你可以通過它儲存各種各樣的日誌,你可以通過它儲存各種格式的日誌,logging的日誌可以分為debug(),info(),warning(),error() 和 critical()五個層級。
預設情況下Python的logging模組將日誌列印到了標準輸出中,且只顯示了大於等於WARNING層級的日誌,這說明預設的記錄層級設定為WARNING(記錄層級等級CRITICAL > ERROR > WARNING > INFO > DEBUG),預設的日誌格式為記錄層級:Logger名稱:使用者輸出訊息。
一,logging模組簡介
logging模組是Python內建的標準模組,主要用於輸出作業記錄,可以設定輸出日誌的等級、日誌儲存路徑、記錄檔復原等;相比print,具備如下優點:
- 可以通過設定不同的日誌等級,在release版本中只輸出重要訊息,而不必顯示大量的調試資訊;
- print將所有資訊都輸出到標準輸出中,嚴重影響開發人員從標準輸出中查看其它資料;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 messageERROR:root:error messageCRITICAL:root:critical message
可見,預設情況下Python 的 logging 模組將日誌列印到了標準輸出中,且只顯示了大於等於WARNING層級的日誌,這說明預設的記錄層級設定為WARNING(記錄層級等級CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET),預設的日誌格式為記錄層級:Logger名稱:使用者輸出訊息。
三,靈活配置記錄層級,日誌格式,輸出位置
import logging logging.basicConfig(level=logging.DEBUG, format=‘%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s‘, datefmt=‘%a, %d %b %Y %H:%M:%S‘, filename=‘test.log‘, filemode=‘w‘) logging.debug(‘debug message‘) logging.info(‘info message‘) logging.warning(‘warning message‘) logging.error(‘error message‘) logging.critical(‘critical message‘)
在test.log中查看輸出:
Wed, 14 Mar 2018 09:39:09 log模組.py[line:24] DEBUG debug messageWed, 14 Mar 2018 09:39:09 log模組.py[line:25] INFO info messageWed, 14 Mar 2018 09:39:09 log模組.py[line:26] WARNING warning messageWed, 14 Mar 2018 09:39:09 log模組.py[line:27] ERROR error messageWed, 14 Mar 2018 09:39:09 log模組.py[line:28] CRITICAL critical message
可見在logging.basicConfig()函數中可通過具體參數來更改logging模組預設行為,可用參數有
filename:用指定的檔案名稱建立FiledHandler(後邊會具體講解handler的概念), 這樣日誌會被儲存在指定的檔案中。filemode:檔案開啟檔案,在指定了filename時使用這個參數,預設值為“a”還可指定為“w”。format:指定handler使用的日誌顯示格式。 datefmt:指定日期時間格式。 level:設定rootlogger(後邊會講解具體概念)的記錄層級 stream:用指定的stream建立StreamHandler。可以指定輸出到sys.stderr,sys.stdout 或者檔案(f=open(‘test.log‘,‘w‘)),預設為sys.stderr。 若同時列出了filename和stream兩個參數,則stream參數會被忽略。format參數中可能用到的格式化串:%(name)s Logger的名字%(levelno)s 數字形式的記錄層級%(levelname)s 文本形式的記錄層級%(pathname)s 調用日誌輸出函數的模組的完整路徑名,可能沒有%(filename)s 調用日誌輸出函數的模組的檔案名稱%(module)s 調用日誌輸出函數的模組名%(funcName)s 調用日誌輸出函數的函數名%(lineno)d 調用日誌輸出函數的語句所在的程式碼%(created)f 目前時間,用UNIX標準的表示時間的浮 點數表示%(relativeCreated)d 輸出日誌資訊時的,自Logger建立以 來的毫秒數%(asctime)s 字串形式的目前時間。預設格式是 “2003-07-08 16:49:45,896”。逗號後面的是毫秒%(thread)d 線程ID。可能沒有%(threadName)s 線程名。可能沒有%(process)d 進程ID。可能沒有%(message)s使用者輸出的訊息
四,logger對象
上述幾個例子中我們瞭解到了logging.debug()、logging.info()、logging.warning()、logging.error()、logging.critical()(分別用以記錄不同層級的日誌資訊),logging.basicConfig()(用預設日誌格式(Formatter)為日誌系統建立一個預設的流處理器(StreamHandler),設定基礎配置(如記錄層級等)並加到root logger(根Logger)中)這幾個logging模組層級別的函數,另外還有一個模組層級別的函數是logging.getLogger([name])(返回一個logger對象,如果沒有指定名字將返回root logger)
先看一個最簡單的過程:
# _*_ coding: utf-8 _*_ import logginglogger = logging.getLogger()# 建立一個handler,用於寫入記錄檔,檔案輸出對象fh = logging.FileHandler(‘test1.log‘)# 再建立一個handler,用於輸出到控制台 ,螢幕輸出對象ch = logging.StreamHandler()#format 是輸出格式formatter = logging.Formatter(‘%(asctime)s - %(name)s - %(levelname)s - %(message)s‘)fh.setFormatter(formatter)ch.setFormatter(formatter)logger.addHandler(fh) #logger對象可以添加多個fh和ch對象logger.addHandler(ch)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‘)
結果:
2018-03-14 10:19:39,478 - root - WARNING - logger warning message2018-03-14 10:19:39,478 - root - ERROR - logger error message2018-03-14 10:19:39,478 - root - CRITICAL - logger critical message
4.1 將日誌寫入檔案
設定logging,建立一個FileHandler,並對輸出訊息的格式進行設定,將其添加到logger,然後將日誌寫入到指定的檔案中,
import logginglogger = logging.getLogger(__name__)logger.setLevel(level = logging.INFO)handler = logging.FileHandler("log.txt")handler.setLevel(logging.INFO)formatter = logging.Formatter(‘%(asctime)s - %(name)s - %(levelname)s - %(message)s‘)handler.setFormatter(formatter)logger.addHandler(handler)logger.info("Start print log")logger.debug("Do something")logger.warning("Something maybe fail.")logger.info("Finish")
log.txt中日誌輸出資料為:
2018-03-14 22:47:14,497 - __main__ - INFO - Start print log2018-03-14 22:47:14,523 - __main__ - WARNING - Something maybe fail.2018-03-14 22:47:14,523 - __main__ - INFO - Finish
五,日誌同時輸出到螢幕和檔案
如果想同時把log列印到螢幕和檔案日誌裡面,就需要瞭解一點複雜的知識了
python使用logging模組記錄日誌涉及及格主要類,使用官方文檔中的概括最為合適:
- logger提供了應用程式可以直接使用的介面
- handle將(logger建立的)日誌記錄發送到合適的目的輸出
- filter提供了細節裝置來決定輸出哪條日誌記錄
- formatter決定日誌記錄的最終輸出格式
他們之間的關係如下:
logger
每個程式在輸出資訊之前都要獲得一個Logger,Logger通常對應了程式的模組名,比如聊天工具的圖形介面模組可以這樣獲得它的Logger:
LOG = logging.getLogger("chat,gui")
而核心模組可以這樣:
LOG=logging.getLogger(”chat.kernel”)
還可以綁著handle 和 filters
Logger.setLevel(lel):指定最低的記錄層級,低於lel的層級將被忽略。 debug是最低的內建層級,critical為最高Logger.addFilter(filt)、Logger.removeFilter(filt):添加或刪除指定的filterLogger.addHandler(hdlr)、Logger.removeHandler(hdlr):增加或刪除指定的handler
Logger.debug()、Logger.info()、Logger.warning()、Logger.error()、Logger.critical():可以設定的記錄層級
handler
handler對象負責發送相關的資訊到指定目的地,Python的日誌系統有多種Handler可以使用,有些handler可以把資訊輸出到控制台,有些Handler可以把資訊輸出到檔案,還有些Handler可以把資訊發送到網路上,如果覺得不夠用,還可以編寫自己的Handler,可以通過addHandler()方法添加多個Handler
Handler.setLevel(lel):指定被處理的資訊層級,低於lel層級的資訊將被忽略Handler.setFormatter():給這個handler選擇一個格式Handler.addFilter(filt)、Handler.removeFilter(filt):新增或刪除一個filter對象
每個Logger可以附加多個Handler。接下來我們就來介紹一些常用的Handler:
1,logging.StreamHandler 使用這個Handler可以向類似與sys.stdout或者sys.stderr的任何檔案對象(file object)輸出資訊。
2,logging.FileHandler 和StreamHandler 類似,用於向一個檔案輸出日誌資訊。不過FileHandler會幫你開啟這個檔案
3,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並不會被更名,而是被刪除。
4,logging.handlers.TimedRotatingFileHandler
這個Handler和RotatingFileHandler類似,不過,它沒有通過判斷檔案大小來決定何時重新建立記錄檔,而是間隔一定時間就 自動建立新的記錄檔。重新命名的過程與RotatingFileHandler類似,不過新的檔案不是附加數字,而是目前時間。它的函數是:
TimedRotatingFileHandler( filename [,when [,interval [,backupCount]]])
其中filename參數和backupCount參數和RotatingFileHandler具有相同的意義。
interval是時間間隔
when參數是一個字串,表示時間間隔的單位,不區分大小寫,它有以下取值:
S 秒M 分H 小時D 天W 每星期(interval==0時代表星期一)midnight 每天淩晨
formatter組件
日誌的formatter是個獨立的組件,可以根handler組合
fh = logging.FileHandler("access.log")formatter = logging.Formatter(‘%(asctime)s - %(name)s - %(levelname)s - %(message)s‘)fh.setFormatter(formatter) #把formmater綁定到fh上
filter組件
如果你相對記錄檔內容進行過濾,就可以自訂一個filter
class IgnoreBackupLogFilter(logging.Filter): """忽略帶db backup 的日誌""" def filter(self, record): #固定寫法 return "db backup" not in record.getMessage()
注意filter函數會返加True or False,logger根據此值決定是否輸出此日誌
然後把這個filter添加到logger中
logger.addFilter(IgnoreBackupLogFilter())
下面的日誌就會把符合filter條件的過濾掉
logger.debug("test ....")logger.info("test info ....")logger.warning("start to run db backup job ....")logger.error("test error ....")六,應用執行個體 6.1 一個同時輸出到螢幕、檔案、帶filter的完成例子
import loggingclass IgnoreBackupLogFilter(logging.Filter): """忽略帶db backup 的日誌""" def filter(self, record): #固定寫法 return "db backup" not in record.getMessage()#console handlerch = logging.StreamHandler()ch.setLevel(logging.INFO)#file handlerfh = logging.FileHandler(‘mysql.log‘)#fh.setLevel(logging.WARNING)#formatterformatter = logging.Formatter(‘%(asctime)s - %(name)s - %(levelname)s - %(message)s‘)#bind formatter to chch.setFormatter(formatter)fh.setFormatter(formatter)logger = logging.getLogger("Mysql")logger.setLevel(logging.DEBUG) #logger 優先順序高於其它輸出途徑的#add handler to logger instancelogger.addHandler(ch)logger.addHandler(fh)#add filterlogger.addFilter(IgnoreBackupLogFilter())logger.debug("test ....")logger.info("test info ....")logger.warning("start to run db backup job ....")logger.error("test error ....")
6.2 檔案自動截斷例子
import loggingfrom logging import handlerslogger = logging.getLogger(__name__)log_file = "timelog.log"#fh = handlers.RotatingFileHandler(filename=log_file,maxBytes=10,backupCount=3)fh = handlers.TimedRotatingFileHandler(filename=log_file,when="S",interval=5,backupCount=3)formatter = logging.Formatter(‘%(asctime)s %(module)s:%(lineno)d %(message)s‘)fh.setFormatter(formatter)logger.addHandler(fh)logger.warning("test1")logger.warning("test12")logger.warning("test13")logger.warning("test14")
6.3 應用到atm程式中記錄檔的例子
import osimport timeimport loggingfrom config import settingsdef get_logger(card_num, struct_time): if struct_time.tm_mday < 23: file_name = "%s_%s_%d" %(struct_time.tm_year, struct_time.tm_mon, 22) else: file_name = "%s_%s_%d" %(struct_time.tm_year, struct_time.tm_mon+1, 22) file_handler = logging.FileHandler( os.path.join(settings.USER_DIR_FOLDER, card_num, ‘record‘, file_name), encoding=‘utf-8‘ ) fmt = logging.Formatter(fmt="%(asctime)s : %(message)s") file_handler.setFormatter(fmt) logger1 = logging.Logger(‘user_logger‘, level=logging.INFO) logger1.addHandler(file_handler) return logger1
python logging模組