一、概述 很多程式都有記錄日誌的需求,並且日誌中包含的信息即有正常的程式訪問日誌,還可能有錯誤、警告等信息輸出,python的logging模塊提供了標準的日誌介面,你可以通過它存儲各種格式的日誌,logging的日誌可以分為 debug(), info(), warning(), error() ...
一、概述
很多程式都有記錄日誌的需求,並且日誌中包含的信息即有正常的程式訪問日誌,還可能有錯誤、警告等信息輸出,python的logging模塊提供了標準的日誌介面,你可以通過它存儲各種格式的日誌,logging的日誌可以分為 debug()
, info()
, warning()
, error()
and critical() 5個級別,其中他們級別大小關係
debug() <
info()
<warning()
<error()
<critical() 級別越低列印的日記等級就越多
下麵我們看一下他是怎樣的用法。
二、瞭解用法
1、最簡單的用法
import logging logging.debug("logging debug") logging.info("logging info") logging.warning("user [qianduoduo] attempted wrong password more than 3 times") logging.error("logging error") logging.critical("logging critical") #輸出 WARNING:root:user [qianduoduo] attempted wrong password more than 3 times ERROR:root:logging error CRITICAL:root:logging critical #root 就是預設的用戶名
重點:為什麼上面debug和info沒有輸出,那是因為一個模塊預設的日誌級別是warning,比他級別低的就不會輸出
2、日誌級別
看一下這幾個日誌級別分別代表什麼意思,如表:
Level | When it’s used |
---|---|
DEBUG |
詳細的信息,通常只有在診斷問題時才感興趣。 |
INFO |
確認事情按預期工作。 |
WARNING |
表明發生了意外的事情,或預示在不久的將來會出現一些問題(例如“磁碟空間低”)。該軟體仍按預期運行。 |
ERROR |
由於一個更嚴重的問題,軟體無法執行某些功能。 |
CRITICAL |
一個嚴重的錯誤,表明程式本身可能無法繼續運行。 |
3、將日記寫入文件
import logging logging.basicConfig(filename='duoduo.log',level=logging.INFO) logging.debug('This message should go to the log file') logging.info('So should this') logging.warning('And this, too') logging.warning('And this, too') #‘duoduo.log’文件輸出 INFO:root:So should this WARNING:root:And this, too #這日記的級別設置在info,所以比他更低的debug不顯示
重點: 如果我們想顯示所有級別的日記就要把日記的等級設置為debug,就在level=logging.DEBUG,設置是級別的名稱要大寫
4、加入日期格式
感覺上面的日誌格式忘記加上時間啦,日誌不知道時間怎麼行呢,下麵就來加上
import logging logging.basicConfig(filename='duoduo.log', level=logging.DEBUG, format='%(asctime)s %(message)s',#asctime字元串形式的當前時間,message用戶輸出的消息 datefmt='%Y-%m-%d %I:%M:%S %p ') logging.debug("logging debug") logging.info("logging info") logging.warning("user [qianduoduo] attempted wrong password more than 3 times") logging.error("logging error") logging.critical("logging critical") #輸出到文件‘duoduo.log’ 2018-01-27 07:33:30 PM logging debug 2018-01-27 07:33:30 PM logging info 2018-01-27 07:33:30 PM user [qianduoduo] attempted wrong password more than 3 times 2018-01-27 07:33:30 PM logging error 2018-01-27 07:33:30 PM logging critical
5、 format的日誌格式
|
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 |
用戶輸出的消息 |
三、log的用法,大約瞭解這個操作步驟
如果想同時把log列印在屏幕和文件日誌里,就需要瞭解一點複雜的知識 了
Python 使用logging模塊記錄日誌涉及四個主要類,使用官方文檔中的概括最為合適:
1、logger提供了應用程式可以直接使用的介面;
2、handler將(logger創建的)日誌記錄發送到合適的目的輸出;
3、filter提供了細度設備來決定輸出哪條日誌記錄;
4、formatter決定日誌記錄的最終輸出格式。
1)、logger
①每個程式在輸出信息之前都需要獲得一個logger。logger通常對應了程式的模塊名,比如聊天工具的圖形界面模塊可以這樣獲得它的logger:
1 |
logger = logging.getLogger( "chat.gui" )
|
核心模塊可以這樣寫:(自己想叫什麼名稱就叫什麼名稱)
1 |
logger = logging.getLogger( "chat.kernel" )
|
②logger.setLevel(lel)
說明:指定最低的日誌級別,低於lel的級別將被忽略(debug是最低的內置級別,critical為最高)
1 |
logger.setLevel(logging.DEBUG) #設置級別為debug級別
|
③Logger.addFilter(filt)、Logger.removeFilter(filt)
說明:添加或刪除指定的filter
④logger.addHandler(hdlr)、logger.removeHandler(hdlr)
說明:增加或刪除指定的handler
1 2 3 |
logger.addHandler(ch) #添加handler
logger.removeHandler(ch) #刪除handler
|
⑤Logger.debug()、Logger.info()、Logger.warning()、Logger.error()、Logger.critical()
說明:可以設置的日誌級別
1 2 3 4 5 |
logger.debug( 'debug message' )
logger.info( 'info message' )
logger.warn( 'warn message' )
logger.error( 'error message' )
logger.critical( 'critical message' )
|
⑥獲取handler個數
1 2 3 4 5 |
handler_len = len (logger.handlers)
print (handler_len)
#輸出
1
|
2)、handler
handler對象負責發送相關的信息到指定目的地。Python的日誌系統有多種Handler可以使用。有些Handler可以把信息輸出到控制台,有些Logger可以把信息輸出到文件,還有些 Handler可以把信息發送到網路上。如果覺得不夠用,還可以編寫自己的Handler。可以通過addHandler()方法添加多個多handler
①Handler.setLevel(lel)
說明:指定被處理的信息級別,低於lel級別的信息將被忽略。
1 2 |
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
|
②Handler.setFormatter()
說明:給這個handler選擇一個格式
1 2 3 |
ch_formatter = logging.Formatter( "%(asctime)s - %(name)s - %(levelname)s - %(message)s" ) #生成格式,具體需要什麼格式看需求
ch.setFormatter(ch_formatter) #設置格式
|
③Handler.addFilter(filt)、Handler.removeFilter(filt)
說明:新增或刪除一個filter對象
四、handler詳解
1、logging.StreamHandler
說明:使用這個Handler可以向類似與sys.stdout或者sys.stderr的任何文件對象(file object)輸出信息,也就是屏幕輸出。
它的構造函數是:StreamHandler([strm]),其中strm參數是一個文件對象,預設是sys.stderr。具體用法看下麵代碼:
import logging logger = logging.getLogger("TEST-LOG") logger.setLevel(logging.DEBUG) ch = logging.StreamHandler() #創建一個StreamHandler對象 ch.setLevel(logging.DEBUG) #設置輸出StreamHandler日誌級別 ch_formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") #格式 ch.setFormatter(ch_formatter) #傳入格式參數 logger.addHandler(ch) #增加handler logger.info('info message') logger.warn('warn message') logger.error('error message') logger.critical('critical message') # 輸出 #2018-01-28 21:15:02,112 - TEST-LOG - INFO - info message #2018-01-28 21:15:02,112 - TEST-LOG - WARNING - warn message #2018-01-28 21:15:02,112 - TEST-LOG - ERROR - error message #2018-01-28 21:15:02,112 - TEST-LOG - CRITICAL - critical message
2、logging.FileHandler
說明:和StreamHandler類似,用於向一個文件輸出日誌信息,不過FileHandler會幫你打開這個文件。
它的構造函數是:FileHandler(filename[,mode])。filename是文件名,必須指定一個文件名。mode是文件的打開方式。參見Python內置函數open()的用法。預設是’a',即添加到文件末尾。用法看下麵代碼:
import logging #create logging logger = logging.getLogger("TEST-LOG") logger.setLevel(logging.DEBUG) fh = logging.FileHandler("debug.log",encoding="utf-8") #日誌輸出到debug.log文件中 fh.setLevel(logging.INFO) #設置FileHandler日誌級別 fh_formatter = logging.Formatter("%(asctime)s %(module)s:%(levelname)s %(message)s") fh.setFormatter(fh_formatter) logger.addHandler(fh) logger.info('info message') logger.warning('warning message') logger.error('error message') logger.critical('critical message') #文件debug.log里的輸出: #2018-01-28 21:19:45,221 logging文件輸出:INFO info message #2018-01-28 21:19:45,221 logging文件輸出:WARNING warning message #2018-01-28 21:19:45,221 logging文件輸出:ERROR error message #2018-01-28 21:19:45,221 logging文件輸出:CRITICAL critical message
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並不會被更名,而是被刪除。
用法看下麵代碼:
import logging from logging import handlers #需要導入handlers logger = logging.getLogger(__name__) log_file = "timelog.log" #按文件大小來分割,10個位元組maxBytes,保留個數是3個 fh=handlers.RotatingFileHandler(filename=log_file,maxBytes=10,backupCount=3,encoding="utf-8") formatter = logging.Formatter('%(asctime)s %(module)s: %(message)s') fh.setFormatter(formatter) logger.addHandler(fh) logger.warning("test11") logger.warning("test12") logger.warning("test13") logger.warning("test14") logger.warning("test15") #文件的結果就是 #timelog.log 2018-01-28 21:31:38,513 logging文件個數保留: test15 #timelog.log1 2018-01-28 21:31:38,500 logging文件個數保留: test14 #timelog.log2 2018-01-28 21:31:38,487 logging文件個數保留: test13 #timelog.log3 2018-01-28 21:31:38,471 logging文件個數保留: test12
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:每天凌晨
5、控制台和文件日誌共同輸出
需要什麼樣的輸出,只需要添加相應的handler就ok了。
邏輯圖:
代碼如下:
import logging #create logging logger = logging.getLogger("TEST-LOG") logger.setLevel(logging.DEBUG) #屏幕handler ch = logging.StreamHandler() ch.setLevel(logging.DEBUG) #文件handler fh = logging.FileHandler("debug.log",encoding="utf-8") fh.setLevel(logging.INFO) #分別創建輸出日誌格式 ch_formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") fh_formatter = logging.Formatter("%(asctime)s %(module)s:%(levelname)s %(message)s") #設置handler的輸出格式 ch.setFormatter(ch_formatter) fh.setFormatter(fh_formatter) #添加handler logger.addHandler(ch) logger.addHandler(fh) # 'application' code logger.debug('debug message') logger.info('info message') logger.warn('warn message') logger.error('error message') logger.critical('critical message')
註:如果添加時間分割或者文件大小分割,再修改上述代碼寫入文件的handler即可。
五、運用模板做日記功能
瞭解記住上面的流程,我們來做一個簡單的日記配置(此方法高能)
""" logging配置 """ import os import logging.config # 定義三種日誌輸出格式 開始 standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \ '[%(levelname)s][%(message)s]' #其中name為getlogger指定的名字 simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s' id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s' # 定義日誌輸出格式 結束 logfile_dir = os.path.dirname(os.path.abspath(__file__)) # log文件的目錄 logfile_name = 'all2.log' # log文件名 # 如果不存在定義的日誌目錄就創建一個 if not os.path.isdir(logfile_dir): os.mkdir(logfile_dir) # log文件的全路徑 logfile_path = os.path.join(logfile_dir, logfile_name) # log配置字典 LOGGING_DIC = { 'version': 1, 'disable_existing_loggers': False, #前面兩個暫時不關註 'formatters': { 'standard': { 'format': standard_format }, 'simple': { 'format': simple_format }, }, 'filters': {}, 'handlers': { #列印到終端的日誌 'console': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', # 列印到屏幕 'formatter': 'simple' }, #列印到文件的日誌,收集info及以上的日誌 'default': { 'level': 'DEBUG', 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件 'formatter': 'standard', 'filename': logfile_path, # 日誌文件 'maxBytes': 1024*1024*5, # 日誌大小 5M 'backupCount': 5, 'encoding': 'utf-8', # 日誌文件的編碼,再也不用擔心中文log亂碼了 }, }, 'loggers': { #logging.getLogger(__name__)拿到的logger配置 '': { #這裡可以匹配相同的key,然後輸出,還有空,預設都可以輸出 'handlers': ['default', 'console'], # 這裡把上面定義的兩個handler都加上,即log數據既寫入文件又列印到屏幕 'level': 'DEBUG', 'propagate': True, # 向上(更高level的logger)傳遞 }, }, } def load_my_logging_cfg(): logging.config.dictConfig(LOGGING_DIC) # 導入上面定義的logging配置 logger = logging.getLogger(__name__) # 生成一個log實例 logger.info('It works!') # 記錄該文件的運行狀態 if __name__ == '__main__': load_my_logging_cfg()