日志模塊
logging
logging模塊主要可以根據自定義日志信息,在程序運行的時候將日志打印在終端及記錄日志到文件中。在這先了解一下logging支持的日志五個級別
debug() 調試級別,一般用於記錄程序運行的詳細信息
info() 事件級別,一般用於記錄程序的運行過程
warnning() 警告級別,,一般用於記錄程序出現潛在錯誤的情形
error() 錯誤級別,一般用於記錄程序出現錯誤,但不影響整體運行
critical 嚴重錯誤級別 , 出現該錯誤已經影響到整體運行
- 簡單用法,將日志打印到終端
#!/usr/bin/env python # -*- coding: UTF-8 -*- #pyversion:python3.5 #owner:fuzj import logging logging.debug("User %s is loging" % 'jeck') logging.info("User %s attempted wrong password" % 'fuzj') logging.warning("user %s attempted wrong password more than 3 times" % 'mary') logging.error("select db is timeout") logging.critical("server is down")
輸出結果:
WARNING:root:user mary attempted wrong password more than 3 times ERROR:root:select db is timeout CRITICAL:root:server is down
發現怎么沒有debug和info的日志呢? 原來默認情況下,logging將日志打印到屏幕,日志級別為WARNING。而debug和info在warnning級別之下,所以不打印。我們可以調整一下終端輸出級別,為美觀一下,自定義日志格式
#!/usr/bin/env python # -*- coding: UTF-8 -*- #pyversion:python3.5 #owner:fuzj 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') logging.debug("User %s is loging" % 'jeck') logging.info("User %s attempted wrong password" % 'fuzj') logging.warning("user %s attempted wrong password more than 3 times" % 'mary') logging.error("select db is timeout") logging.critical("server is down")
輸出結果
Thu, 09 Jun 2016 11:42:08 日志.py[line:13] DEBUG User jeck is loging Thu, 09 Jun 2016 11:42:08 日志.py[line:14] INFO User fuzj attempted wrong password Thu, 09 Jun 2016 11:42:08 日志.py[line:15] WARNING user mary attempted wrong password more than 3 times Thu, 09 Jun 2016 11:42:08 日志.py[line:16] ERROR select db is timeout Thu, 09 Jun 2016 11:42:08 日志.py[line:17] CRITICAL server is down
這樣的輸出結果看着還過得去。現在腦補一下上面用到的logging.basicConfig函數
-
logging.basicConfig
主要可以對日志的輸出格式及方式做相關配置支持的參數由:
- filename: 指定日志文件名
- filemode: 和file函數意義相同,指定日志文件的打開模式,'w'或'a'
- SDAD
- format: 指定輸出的格式和內容,format可以輸出很多有用信息,如上例所示:
- %(levelno)s: 打印日志級別的數值
- %(levelname)s: 打印日志級別名稱
- %(pathname)s: 打印當前執行程序的路徑,其實就是sys.argv[0]
- %(filename)s: 打印當前執行程序名
- %(funcName)s: 打印日志的當前函數
- %(lineno)d: 打印日志的當前行號
- %(asctime)s: 打印日志的時間
- %(thread)d: 打印線程ID
- %(threadName)s: 打印線程名稱
- %(process)d: 打印進程ID
- %(message)s: 打印日志信息
- datefmt: 指定時間格式,同time.strftime()
- level: 設置日志級別,默認為logging.WARNING,可選 logging.DEBUG logging.INFO logging.WARNING logging.ERROR logging.CRITICAL
- stream: 指定將日志的輸出流,可以指定輸出到sys.stderr,sys.stdout或者文件,默認輸出到sys.stderr,當stream和filename同時指定時,stream被忽略
-
記錄日志到文件
現在有了新需求,我不能只打印日志到終端呀,我想記錄日志咋辦?
很簡單,使用上面的logging.basicConfig即可搞定
#!/usr/bin/env python # -*- coding: UTF-8 -*- #pyversion:python3.5 #owner:fuzj import logging #在logging.basciConfig中指定文件和寫入方式 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='a') logging.debug("User %s is loging" % 'jeck') logging.info("User %s attempted wrong password" % 'fuzj') logging.warning("user %s attempted wrong password more than 3 times" % 'mary') logging.error("select db is timeout") logging.critical("server is down")
輸出效果:
此沒有終端輸出,發現在同級目錄生成了一個test.log文件,打開看看,原來日志都已經寫進去了,和上面終端輸出的一樣
Thu, 09 Jun 2016 11:57:41 日志.py[line:15] DEBUG User jeck is loging Thu, 09 Jun 2016 11:57:41 日志.py[line:16] INFO User fuzj attempted wrong password Thu, 09 Jun 2016 11:57:41 日志.py[line:17] WARNING user mary attempted wrong password more than 3 times Thu, 09 Jun 2016 11:57:41 日志.py[line:18] ERROR select db is timeout Thu, 09 Jun 2016 11:57:41 日志.py[line:19] CRITICAL server is down
-
同輸出屏幕和記錄日志
現在又有了新需求,我不能只把日志記錄到文件,有些日志信息我還是想直接在屏幕輸出,而且還不影響日志記錄文件,此過程比較復雜,先准備點基礎知識
- logging的四大組件
- Loggers 提供應用程序可直接使用的接口
- Handlers 發送日志到適當的目的地
- Filters 提供了過濾日志信息的方法
- Formatters 指定日志顯示格式
- logging的四大組件
下面看代碼
#!/usr/bin/env python # -*- coding: UTF-8 -*- #pyversion:python3.5 #owner:fuzj import logging logger = logging.getLogger("test.conf") #創建一個logger,默認為root logger logger.setLevel(logging.DEBUG) #設置全局log級別為debug。注意全局的優先級最高 hterm = logging.StreamHandler() #創建一個終端輸出的handler,設置級別為error hterm.setLevel(logging.ERROR) hfile = logging.FileHandler("access.log") #創建一個文件記錄日志的handler,設置級別為info hfile.setLevel(logging.INFO) formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') #創建一個全局的日志格式 hterm.setFormatter(formatter) #將日志格式應用到終端handler hfile.setFormatter(formatter) #將日志格式應用到文件handler logger.addHandler(hterm) #將終端handler添加到logger logger.addHandler(hfile) #將文件handler添加到logger #定義日志msg,注意此時是logger,不是logging了 logger.debug("User %s is loging" % 'jeck') logger.info("User %s attempted wrong password" % 'fuzj') logger.warning("user %s attempted wrong password more than 3 times" % 'mary') logger.error("select db is timeout") logger.critical("server is down")
結果:
在終端輸出:
2016-06-09 12:22:25,283 - test.conf - ERROR - select db is timeout 2016-06-09 12:22:25,283 - test.conf - CRITICAL - server is down
在日志文件里寫入:
2016-06-09 12:22:25,283 - test.conf - INFO - User fuzj attempted wrong password 2016-06-09 12:22:25,283 - test.conf - WARNING - user mary attempted wrong password more than 3 times 2016-06-09 12:22:25,283 - test.conf - ERROR - select db is timeout 2016-06-09 12:22:25,283 - test.conf - CRITICAL - server is down
是不是很高大上?不要激動,這只是簡單實現了而已。logging模塊的功能遠不止這些。下面將深入剖析logging的四大組件
先來干貨,請看下面腳本:
#!/usr/bin/env python # -*- coding: UTF-8 -*- #pyversion:python3.5 #owner:fuzj import logging #創建logger logger = logging.getLogger() #創建默認logger logger1 = logging.getLogger("testlog") #創建一個名為testlog1的logger實例logger1 logger2 = logging.getLogger("testlog") #創建一個名為testlog1的logger實例logger2 logger3 = logging.getLogger("testlog.child") #創建一個testlog子實例logger3 #設置logger的日志級別 logger1.setLevel(logging.DEBUG) #將logger1日志級別設置為debug logger2.setLevel(logging.INFO) #將logger1日志級別設置為info logger3.setLevel(logging.ERROR) #將logger1日志級別設置為warning #創建handler hterm = logging.StreamHandler() #輸出到終端 hfile = logging.FileHandler("test.log") #輸出到文件 #定義日志格式 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') #將日志格式應用到handler hterm.setFormatter(formatter) hfile.setFormatter(formatter) #給logger添加handler logger.addHandler(hterm) logger.addHandler(hfile) logger1.addHandler(hterm) logger1.addHandler(hfile) logger2.addHandler(hterm) logger2.addHandler(hfile) logger3.addHandler(hterm) logger3.addHandler(hfile) #記錄日志信息 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') 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') logger3.debug('logger3 debug message') logger3.info('logger3 info message') logger3.warning('logger3 warning message') logger3.error('logger3 error message') logger3.critical('logger3 critical message')
結果:文件和終端輸出:
2016-06-09 14:55:46,136 - root - WARNING - logger warning message 2016-06-09 14:55:46,136 - root - ERROR - logger error message 2016-06-09 14:55:46,136 - root - CRITICAL - logger critical message 2016-06-09 14:55:46,136 - testlog - INFO - logger1 info message 2016-06-09 14:55:46,136 - testlog - INFO - logger1 info message 2016-06-09 14:55:46,137 - testlog - WARNING - logger1 warning message 2016-06-09 14:55:46,137 - testlog - WARNING - logger1 warning message 2016-06-09 14:55:46,137 - testlog - ERROR - logger1 error message 2016-06-09 14:55:46,137 - testlog - ERROR - logger1 error message 2016-06-09 14:55:46,137 - testlog - CRITICAL - logger1 critical message 2016-06-09 14:55:46,137 - testlog - CRITICAL - logger1 critical message 2016-06-09 14:55:46,137 - testlog - INFO - logger2 info message 2016-06-09 14:55:46,137 - testlog - INFO - logger2 info message 2016-06-09 14:55:46,137 - testlog - WARNING - logger2 warning message 2016-06-09 14:55:46,137 - testlog - WARNING - logger2 warning message 2016-06-09 14:55:46,137 - testlog - ERROR - logger2 error message 2016-06-09 14:55:46,137 - testlog - ERROR - logger2 error message 2016-06-09 14:55:46,137 - testlog - CRITICAL - logger2 critical message 2016-06-09 14:55:46,137 - testlog - CRITICAL - logger2 critical message 2016-06-09 14:55:46,137 - testlog.child - ERROR - logger3 error message 2016-06-09 14:55:46,137 - testlog.child - ERROR - logger3 error message 2016-06-09 14:55:46,137 - testlog.child - ERROR - logger3 error message 2016-06-09 14:55:46,137 - testlog.child - CRITICAL - logger3 critical message 2016-06-09 14:55:46,137 - testlog.child - CRITICAL - logger3 critical message 2016-06-09 14:55:46,137 - testlog.child - CRITICAL - logger3 critical message
- logger
logger一個樹形層級結構,輸出信息之前都要獲得一個Logger,logger = logging.getLogger()返回一個默認的Logger也即root Logger,並應用默認的日志級別、Handler和Formatter設置.
從輸出中發現,
1)root logger由於沒有定義loglevel,所以默認是warning,所以輸出三天記錄
2)logger1 testlog.logger 定義的loglevel是debug,怎么輸出的信息是蟲info開始呢? 原來logger1和logger2對應的同一個logger實例,logger2重新定義了loglevel,所以logger1和logger2對應的實例loglevel級別就成了info,小心在這里出錯
3)從日志條數上看,怎么logger1 和logger2重復輸出了2次,logger2重復輸出了三次呢,原來和logger的結構有關,他是樹形結構,logger = logging.getLogger()顯示的創建了root Logger,而我們繼續創建的logger1 logger2 則是root logger實例的子實例,而logger3是logger1和logger2的子實例
logger2 = logging.getLogger("testlog") logger3 = logging.getLogger("testlog.child")
看出來了吧,logger2對應的是testlog,而logger3對應的是testlog.child。這樣可得知,一個logger不僅需要自己的handler處理消息,還會把消息發送給自己的父logger的handler。,而子logger如果沒有定義日志級別、Handler、Filter等設置,會逐層繼承父logger的設置,所以父logger會優先按子logger的屬性進行處理,否則就按自己的處理,所以就出現了多次重復日志現象
-
handler
負責發送相關的信息到指定目的地
-
常用的Handler方法:
- Handler.setLevel(lel):指定日志級別,低於lel級別的日志將被忽略
- Handler.setFormatter():給這個handler選擇一個Formatter
- Handler.addFilter(filt)、Handler.removeFilter(filt):新增或刪除一個filter對象
-
可用的handler類型有:
- logging.StreamHandler 可以向類似與sys.stdout或者sys.stderr的任何文件對象(file object)輸出信息
- logging.FileHandler 用於向一個文件輸出日志信息
- logging.handlers.RotatingFileHandler 類似於上面的FileHandler,但是它可以管理文件大小。當文件達到一定大小之后,它會自動將當前日志文件改名,然后創建一個新的同名日志文件繼續輸出
- logging.handlers.TimedRotatingFileHandler 和RotatingFileHandler類似,不過,它沒有通過判斷文件大小來決定何時重新創建日志文件,而是間隔一定時間就自動創建新的日志文件
- logging.handlers.SocketHandler 使用TCP協議,將日志信息發送到網絡。
- logging.handlers.DatagramHandler 使用UDP協議,將日志信息發送到網絡。
- logging.handlers.SysLogHandler 日志輸出到syslog
- logging.handlers.NTEventLogHandler 遠程輸出日志到Windows NT/2000/XP的事件日志
- ogging.handlers.SMTPHandler 遠程輸出日志到郵件地址
- logging.handlers.MemoryHandler 日志輸出到內存中的制定buffer
- logging.handlers.HTTPHandler 通過
GET
或POST
遠程輸出到HTTP服務器
具體參考:
https://docs.python.org/3/library/logging.handlers.html#module-logging.handlers -
-
filter
定義handler處理的規則
如定義一個規則
filter=logging.Filter('testlog.child') #符合testlog.child的才能記錄日志到文件 hterm.addFilter(filter) #將filer應用到hterm的handle
執行結果:
終端輸出:
2016-06-09 15:42:30,501 - testlog.child - ERROR - logger3 error message 2016-06-09 15:42:30,501 - testlog.child - ERROR - logger3 error message 2016-06-09 15:42:30,501 - testlog.child - ERROR - logger3 error message 2016-06-09 15:42:30,501 - testlog.child - CRITICAL - logger3 critical message 2016-06-09 15:42:30,501 - testlog.child - CRITICAL - logger3 critical message 2016-06-09 15:42:30,501 - testlog.child - CRITICAL - logger3 critical message
日志輸出不變
此處是給logger加了filer,也可以給handler加,但是影響的酒不止一個logger了。他會影響所有使用這個handler的logger
- formatters
用於定義輸出的日志格式。用到的格式化內容可以參考logging.basicConfig()函數
#定義日志格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
#將日志格式應用到handler
hterm.setFormatter(formatter)
hfile.setFormatter(formatter)
