python日誌處理模塊
- 2020 年 1 月 3 日
- 筆記
一 日誌處理模塊概述
1 日誌級別
日誌級別level |
數值 |
---|---|
CRITICAL |
50 |
ERROR |
40 |
WARNING |
30 ,默認日誌級別 |
INFO |
20 |
DEBUG |
10 |
NOTSET |
0,表示不設置 |
日誌級別是指產生日誌的嚴重程度 設置一個級別後,嚴重程度低於次級別的日誌消息將會被忽略 數字越高,優先級別越高
#!/usr/bin/python3.6 #conding:utf-8 import threading import time import logging logging.basicConfig(level=logging.INFO) #設置日誌級別為info def add(x,y): logging.warning(x+y) # 此處定義的日誌級別是warning,因此能夠被打印出來,默認的日誌打印格式是 級別:用戶:信息 t=threading.Timer(1,add,args=(3,4)) # 此處表示6秒後出結果 t.start()
結果如下

默認的日誌打印格式是級別:用戶:信息
2 格式字符串
1 常見格式
屬性名 |
格式 |
描述 |
---|---|---|
日誌消息內容 |
%(message)s |
當調用formatter.format() 時設置 |
asctime |
%(asctime)s |
創建logrecord時的可讀時間,默認格式是Y-m-d H:M:S, (逗號後面的數字是毫秒部分的時間) |
函數名 |
%(funcName)s |
日誌調用所在的函數名 |
日誌級別名稱 |
%(levelname)s |
消息級別名稱,DEBUG,INFO,WARNING,ERROR,CRITICAL |
日誌級別數值 |
%(levelno)s |
消息的級別數字DEBUG,INFO,WARNING,ERROR,CRITICAL |
行號 |
%(lineno)d |
日誌調用所在源碼行號 |
模塊 |
%(module)s |
模塊(filename的名字部分) |
進程ID |
%(process)d |
進程ID |
線程ID |
%(thread)d |
線程ID |
進程名稱 |
%(processName)s |
進程名 |
線程名稱 |
%(threadName)s |
線程名字 |
d 格式化成數字,s格式化成字符串
#!/usr/bin/python3.6 #conding:utf-8 import threading import time FORMAT="%(asctime)-15stThread Name: %(threadName)st%(message)s" # 此處定義日誌格式 import logging logging.basicConfig(level=logging.INFO,format=FORMAT) #設置日誌級別為info,此處format為引用日誌格式 def add(x,y): logging.warning(x+y) # 此處定義的日誌級別是warning,因此能夠被打印出來 t=threading.Thread(target=add,args=(3,4),name='a1') t.start()
結果如下

2 格式化時間字串
#!/usr/bin/python3.6 #conding:utf-8 import threading import time FORMAT="%(asctime)-15stThread Name: %(threadName)st%(message)s" # 此處定義日誌格式 import logging logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt='%y-%m-%d %H:%M:%S %z') #設置日誌級別為info,此處format為引用日誌格式,此處通過定義日誌格式來 # 確定打印的日誌為正常格式,其和%(asctime)互相作用 def add(x,y): logging.warning(x+y) # 此處定義的日誌級別是warning,因此能夠被打印出來 t=threading.Thread(target=add,args=(3,4),name='a1') t.start()
結果如下

不同打印風格
#!/usr/bin/python3.6 #conding:utf-8 import threading import time FORMAT="%(asctime)-15stThread Name: %(threadName)st%(message)s" # 此處定義日誌格式 import logging logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt='%y-%m-%d %H:%M:%S %z') #設置日誌級別為info,此處format為引用日誌格式,此處通過定義日誌格式來 # 確定打印的日誌為正常格式,其和%(asctime)互相作用 def add(x,y): logging.warning("%d",x+y) # 此處定義的日誌級別是warning,因此能夠被打印出來,此處是c風格處理日誌 logging.info("{} 值為 {}".format(threading.enumerate(),x+y)) # 此處是format方式處理 t=threading.Thread(target=add,args=(3,4),name='a1') t.start()
打印結果如下

3 新變量處理
定義一個新的變量來進行存儲
#!/usr/bin/python3.6 #conding:utf-8 import threading import time FORMAT="%(asctime)s t threadName: %(threadName)s t %(message)s t %(myname1)s" # 此處定義日誌格式,此處定義變量名為myname import logging logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt='%y-%m-%d %H:%M:%S %z') #設置日誌級別為info,此處format為引用日誌格式,此處通過定義日誌格式來 # 確定打印的日誌為正常格式,其和%(asctime)互相作用 d={"myname1":"zhangsan"} #此處定義變量值為zhangsan,是字典格式 def add(x,y): logging.warning("%d",x+y,extra=d) # 此處定義的日誌級別是warning,因此能夠被打印出來,此處是c風格處理日誌,通過此處引用extra 字典得到結果 t=threading.Thread(target=add,args=(3,4),name='a1') t.start()
結果如下

4 輸出到文件
#!/usr/bin/python3.6 #conding:utf-8 import threading import time FORMAT="%(asctime)s t threadName: %(threadName)s t %(message)s t %(myname1)s" # 此處定義日誌格式,此處定義變量名為myname import logging logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt='%y-%m-%d %H:%M:%S %z',filename='/root/test.log') #設置日誌級別為info,此處format為引用日誌格式,此處通過定義日誌格式來 # 確定打印的日誌為正常格式,其和%(asctime)互相作用,此處指定打印的路徑,若不存在,則創建並追加,如此下面便不會再次打印 d={"myname1":"zhangsan"} #此處定義變量值為zhangsan,是字典格式 def add(x,y): logging.warning("%d",x+y,extra=d) # 此處定義的日誌級別是warning,因此能夠被打印出來,此處是c風格處理日誌,通過此處引用extra 字典得到結果 t=threading.Thread(target=add,args=(3,4),name='a1') t.start()
結果如下

其相當於輸出重定向到文件
二 logger 類
1 概述
使用工廠方法返回一個logger實例。 logger.getLogger([name=None)] 指定name,返回一個名稱為name的Logger實例,如果再次使用相同的名字,是實例化一個對象。 未指定name,則返回logger實例,名稱是root。及根Logger Logger 是層次結構的,使用. 點號分割。如'a',或'a.b'或'a.b.c.d',a是a.b的父parent,a.b是a的子child,對於foo來說,名字為foo.bar,foo.bar.baz,foo.bam都是foo的後代。
2 基本實驗
1 打印空name的logger
#!/usr/bin/python3.6 #conding:utf-8 import logging log=logging.getLogger() print (log.name) #打印當前實例的名稱,其默認名稱為root print (type(log))# 打印當前實例的類型
結果如下

2 打印非空name的logger
#!/usr/bin/python3.6 #conding:utf-8 import logging log=logging.getLogger('a') # 此處定義其名稱, print (log.name) #打印當前實例的名稱 print (log,type(log))# 打印當前實例的類型和實例情況
結果如下

3 打印層次化logger
#!/usr/bin/python3.6 #conding:utf-8 import logging #!/usr/bin/poython3.6 #conding:utf-8 import logging root=logging.getLogger() print (root,id(root)) # 此處返回跟和跟地址 log=logging.getLogger('a') # 此處定義其名稱, print (log.name) #打印當前實例的名稱 print (log,type(log),log.parent,id(log),id(log.parent))# 打印當前實例的類型和實例情況及其父類情況,此處打印當前給情況和父根情況 # log1=logging.getLogger('a.b') print (log1.name) print (log1,type(log1),log1.parent,id(log1.parent))# 此處打印當前根父跟情況
結果如下

上述表明,其a.b的父類是a,a的父類是root ,其直接存在父子級別
4 繼承和重用
#!/usr/bin/python3.6 #conding:utf-8 import logging FORMAT="%(asctime)s %(thread)d %(message)s" logging.basicConfig(level=logging.INFO,datefmt='%Y-%m-%d %H:%M:%S',format=FORMAT) root=logging.getLogger() root.warning('my root') log=logging.getLogger('a') # 此處定義其名稱, log.warning('my log') log1=logging.getLogger('a.b') log1.warning('my log1')

5 規範化定義name
#!/usr/bin/python3.6 #conding:utf-8 import logging FORMAT="%(asctime)s %(thread)d %(message)s" logging.basicConfig(level=logging.INFO,datefmt='%Y-%m-%d %H:%M:%S',format=FORMAT) root=logging.getLogger() print (root,id(root)) root.warning('my root') loga=logging.getLogger(__name__) # 此處使用模塊名稱進行定義名字 print (loga,id(loga),loga.name) loga.warning('my loga')# loga.name 也不能在此中打印出來 logb=logging.getLogger("{}.{}".format(__name__,'abcd')) # 此處使用模塊名稱下的具體方法定義名稱,通過format進行字符串的拼接 print (logb,id(logb),logb.name) logb.warning('my logb') # logb.name不能打印出來
結果如下

6 獲取和修改日誌級別
獲取和設置相關級別
#!/usr/bin/python3.6 #conding:utf-8 import logging FORMAT="%(asctime)s %(thread)d %(message)s" logging.basicConfig(level=logging.INFO,datefmt='%Y-%m-%d %H:%M:%S',format=FORMAT) root=logging.getLogger() print (root,id(root)) print (root.getEffectiveLevel())#此處打印日誌級別 loga=logging.getLogger(__name__) # 此處使用模塊名稱進行定義名字 loga.warning('my loga')# loga.name 也不能在此中打印出來 print (loga.getEffectiveLevel())#此處打印日誌級別 logb=logging.getLogger("{}.{}".format(__name__,'abcd')) # 此處使用模塊名稱下的具體方法定義名稱,通過format進行字符串的拼接 logb.warning('my logb') # logb.name不能打印出來 logb.debug('my debug') # 此處的debug日誌將不能被打印出來 print (logb.getEffectiveLevel()) #此處打印日誌級別
結果如下

修改日誌級別並進行配置和獲取
#!/usr/bin/python3.6 #conding:utf-8 import logging FORMAT="%(asctime)s %(thread)d %(message)s" logging.basicConfig(level=logging.INFO,datefmt='%Y-%m-%d %H:%M:%S',format=FORMAT) root=logging.getLogger() print (root,id(root)) print (root.getEffectiveLevel())#此處打印日誌級別 loga=logging.getLogger(__name__) # 此處使用模塊名稱進行定義名字 loga.info('my loga')# loga.name 也不能在此中打印出來 print (root.getEffectiveLevel())#此處打印日誌級別 print (loga.getEffectiveLevel())#此處打印日誌級別 loga.setLevel(28)# 此時日誌級別位於INFO 和 WARNING之間, print (loga.getEffectiveLevel()) # 獲取修改後的日誌級別 print ('+'*20,'logb') logb=logging.getLogger("{}.{}".format(__name__,'abcd')) # 此處使用模塊名稱下的具體方法定義名稱,通過format進行字符串的拼接 logb.info('my logb') # 此處因為自身沒有配置日誌級別,因此繼承了上面的日誌級別,因此,其不能打印 logb.setLevel(10) logb.info('my logb') # 此處因為自身設置了日誌級別,因此其可以打印 logb.warning('my debug') print (logb.getEffectiveLevel()) #此處打印日誌級別
結果如下

7 總結:
全局可以設定,但自己模塊可以根據自己的情況進行調整和修改 上述用於設置模塊和模塊對應函數或類的日誌級別,通過上述的定義可以確定打印日誌的級別和相關的配置情況 向在模塊級別的進行配置和修改日誌的級別設置 想做個性化比較難 重要的主要是level和format 的情況
2 handler
1 概述
handler 控制日誌信息的輸出目的地,可以是控制台,文件 可以單獨設置level 可以單獨設置格式 可以設置過濾器
handler分為 1 StreamHandler # 不指定使用sys.stderr A FileHandler # 文件輸出 B _StderrHandler # 標準輸出 2 NullHandler # 什麼都不做 Handler父類 ,FileHandle和_StderrHandler和NullHandler是子類,子類可以繼承父類的相關屬性
2 handler 和 logger的關係
#!/usr/bin/python3.6 #conding:utf-8 import logging FORMAT="%(asctime)s Threadinfo: %(threadName)s %(message)s" logging.basicConfig(level=logging.INFO,format=FORMAT) log1=logging.getLogger('s') #此處實例化出來一個對象 log1.setLevel(logging.WARNING) #設置此對象的相關屬性 h1=logging.StreamHandler() # 實例化一個標準輸出的handler h1.setLevel(logging.INFO) # 設置handler的級別 log1.addFilter(h1) # 將h1加入到log1 log1.info('info log1') # 打印INFO日誌。 log1.warning('warning log1') # 打印WARING ,此處只能打印WAENING
結果如下

此處表明,雖然handler設置了info級別,但其沒不能影響log1的設置,仍然不能打印info級別
#!/usr/bin/python3.6 #conding:utf-8 import logging FORMAT="%(asctime)s Threadinfo: %(threadName)s %(message)s" logging.basicConfig(level=logging.INFO,format=FORMAT) log1=logging.getLogger('s') #此處實例化出來一個對象 log1.setLevel(logging.INFO) #設置此對象的相關屬性 h1=logging.StreamHandler() # 實例化一個標準輸出的handler h1.setLevel(logging.ERROR) # 設置handler的級別 log1.addFilter(h1) # 將h1加入到log1 log1.warning('warning log1') # 打印WARING ,此處只能打印WAENING log1.info('info log1') log2=logging.getLogger('s.s1') #此處繼承了s1的屬性 print (log2.getEffectiveLevel()) log2.warning('wangring log2') #此處設置的warning 和handler中設置的ERROR不衝突,因為ERROR 不針對當前設置
結果如下

此處雖然handler設置了ERROR 級別,但info級別還是能夠打印出來,其不會影響當前級別的日誌,而繼承的日誌也沒受到handler的影響直接打印 日誌輸出到文件
#!/usr/bin/python3.6 #conding:utf-8 import logging FORMAT="%(asctime)s Threadinfo: %(threadName)s %(message)s" logging.basicConfig(level=logging.INFO,format=FORMAT) log1=logging.getLogger('s') #此處實例化出來一個對象 log1.setLevel(logging.INFO) #設置此對象的相關屬性 h1=logging.FileHandler('/root/test1.log') # 實例化一個文件輸出的handler h1.setLevel(logging.ERROR) # 設置handler的級別 log1.addHandler(h1) # 將h1加入到log1 log1.warning('warning log1') # 打印WARING ,此處只能打印WAENING log1.info('info log1') log1.error('error log1') #打印error級別
結果如下

文件中結果如下

#!/usr/bin/python3.6 #conding:utf-8 import logging FORMAT="%(asctime)s Threadinfo: %(threadName)s %(message)s" logging.basicConfig(level=logging.INFO,format=FORMAT) log1=logging.getLogger('s') #此處實例化出來一個對象 log1.setLevel(logging.WARNING) #設置此對象的相關屬性 h1=logging.FileHandler('/root/test1.log') # 實例化一個文件輸出的handler h1.setLevel(logging.INFO) # 設置handler的級別 log1.addHandler(h1) # 將h1加入到log1 log1.warning('warning log1') # 打印WARING ,此處只能打印WAENING log1.info('info log1')
結果如下

文件結果

#!/usr/bin/python3.6 #conding:utf-8 import logging FORMAT="%(asctime)s Threadinfo: %(threadName)s %(message)s" logging.basicConfig(level=logging.INFO,format=FORMAT) log1=logging.getLogger('s') #此處實例化出來一個對象 log1.setLevel(logging.WARNING) #設置此對象的相關屬性 h1=logging.FileHandler('/root/test1.log') # 實例化一個文件輸出的handler h1.setLevel(logging.INFO) # 設置handler的級別 log1.addHandler(h1) # 將h1加入到log1 log1.warning('warning log1') # 打印WARING ,此處只能打印WAENING log1.info('info log1') log2=logging.getLogger('s.s1') print (log2.getEffectiveLevel()) log2.info('log2 info')

文件結果

#!/usr/bin/python3.6 #conding:utf-8 import logging FORMAT="%(asctime)s Threadinfo: %(threadName)s %(message)s" logging.basicConfig(level=logging.INFO,format=FORMAT) log1=logging.getLogger('s') #此處實例化出來一個對象 log1.setLevel(logging.INFO) #設置此對象的相關屬性 h1=logging.FileHandler('/root/test1.log') # 實例化一個文件輸出的handler h1.setLevel(logging.INFO) # 設置handler的級別 log1.addHandler(h1) # 將h1加入到log1 log1.warning('warning log1') # 打印WARING ,此處只能打印WAENING log1.info('info log1') log2=logging.getLogger('s.s1') print (log2.getEffectiveLevel()) log2.info('log2 info')
結果

文件結果

3 添加格式化處理
#!/usr/bin/python3.6 #conding:utf-8 import logging FORMAT="%(asctime)s Threadinfo: %(threadName)s %(message)s" logging.basicConfig(level=logging.INFO,format=FORMAT) log1=logging.getLogger('s') #此處實例化出來一個對象 log1.setLevel(logging.INFO) #設置此對象的相關屬性 h1=logging.FileHandler('/root/test1.log') # 實例化一個文件輸出的handler h1.setLevel(logging.INFO) # 設置handler的級別 fmtr=logging.Formatter("%(asctime)s Threadinfo: %(threadName)s %(message)s %(thread)s") h1.setFormatter(fmtr) log1.addHandler(h1) # 將h1加入到log1 log1.warning('warning log1') # 打印WARING ,此處只能打印WAENING log1.info('info log1') log2=logging.getLogger('s.s1') print (log2.getEffectiveLevel()) log2.info('log2 info')
結果如下

文件結果如下

4 handlers 和 formatter
import logging root=logging.getLogger() root.setLevel(logging.ERROR) print ('root',root.handlers) #打印handler列表 h0=logging.StreamHandler() h0.setLevel(logging.WARNING) root.addHandler(h0) print ('root',root.handlers) # 打印列表 for h in root.handlers: print ("root handler = {} ,formatter= {}".format(h,h.formatter)) # 打印默認的formatter
結果如下

import logging root=logging.getLogger() root.setLevel(logging.ERROR) print ('root',root.handlers) #打印handler列表 h0=logging.StreamHandler() h0.setLevel(logging.WARNING) root.addHandler(h0) print ('root',root.handlers) # 打印列表 for h in root.handlers: print ("root handler = {} ,formatter= {}".format(h,h.formatter)) # 打印默認的formatter log1=logging.getLogger('s') log1.setLevel(logging.ERROR) h1=logging.FileHandler('/root/test2.log') log1.addHandler(h1) print ('log1',log1.handlers) log2=logging.getLogger('s.s1') log2.setLevel(logging.CRITICAL) h2=logging.FileHandler('/root/test2.log') h2.setLevel(logging.WARNING) print ('log2 formatter',h2.formatter) # handler 默認無Formatter f2=logging.Formatter("log2 %(name)s %(asctime)s %(message)s") h2.setFormatter(f2) print ('log2.formatter',h2.formatter) log2.addHandler(h2) print ('log2',log2.handlers)
結果為

文件結果為

5 總結:
1 每一個logger實例的level如同入水口,讓水流進來,如果這個門檻太高,水流就不能進來,其相關低級別的信息一定不能被打印出來
2 如果level沒有設置,則使用父類logger的,如果父類沒有,則繼續尋找父類的,最終找到root,而root的默認設置是WARNING
3 消息傳遞流程 在某個logger上產生某種級別的消息,首先和logger的level檢查,如果消息level低於logger的EffectiveLevel有效級別,則丟棄消息,如果通過(大於等於)檢查後,消息交給所有的handler處理,每一個handler需要和自己的level比較來決定是否處理,如果沒有一個handler,或者消息已經被處handler處理過了,則需要通過本logger的propagate屬性是否是True,True則會把這個消息繼續傳遞給父logger,父logger成為新的logger。新的logger直接把消息交給新的logger的所有handler,handler都處理完了,如果paragate屬性是True,新的logger的父成為新的logger,它的所有handler處理消息。
4 logger 實例初始化的paragate屬性為True,及允許向父傳遞logger消息
5 logging.basicConfig
如果root沒有handler,就默認創建一個StreamHandler ,如果設置了filename,則就創建了一個FileHandler,如果設置了format參數,它就會生成一個formatter對象,並把這個formatter加入到剛才創建的handler上,然後把這些handler加入到root.handlers列表上,level是設置給root logger的。如果root.handlers列表不是空,則logging.basicConfig的調用什麼都不做。
6 filter過濾器
可以為handler增加過濾器,所以過濾器隻影響某一個handler,不會影響整個處理流程
import logging FORMAT="%(asctime)s %(thread)d %(message)s" logging.basicConfig(level=logging.ERROR,format=FORMAT,datefmt="%y-%m-%d %H:%M%S %z") #重新格式化傳值,定義日誌輸出到文件 root=logging.getLogger() print (root,id(root)) loga=logging.getLogger('s') # 模塊和模塊下的某個函數或類 print (loga,id(loga),id(loga.parent)) loga.setLevel(logging.INFO) print (loga.getEffectiveLevel()) #默認對應的是常量20 hadr=logging.FileHandler('/root/loga.txt') # 定義,不指定則是標準錯誤輸出 hadr.setLevel(logging.INFO) fmtr=logging.Formatter("%(asctime)s %(thread)d %(threadName)s %(message)s") #此處是一個實例。此處可以自己定義 hadr.setFormatter(fmtr) fltr=logging.Filter('s') # 此處設置過濾器為s,則為s,s.s1均可通過 hadr.addFilter(fltr) loga.addHandler(hadr) logb=logging.getLogger('s.s1') logb.getEffectiveLevel() logb.info('logb info')
結果如下

文件結果如下

import logging FORMAT="%(asctime)s %(thread)d %(message)s" logging.basicConfig(level=logging.ERROR,format=FORMAT,datefmt="%y-%m-%d %H:%M%S %z") #重新格式化傳值,定義日誌輸出到文件 root=logging.getLogger() print (root,id(root)) loga=logging.getLogger('s') # 模塊和模塊下的某個函數或類 print (loga,id(loga),id(loga.parent)) loga.setLevel(logging.INFO) print (loga.getEffectiveLevel()) #默認對應的是常量20 hadr=logging.FileHandler('/root/loga.txt') # 定義,不指定則是標準錯誤輸出 hadr.setLevel(logging.INFO) fmtr=logging.Formatter("%(asctime)s %(thread)d %(threadName)s %(message)s") #此處是一個實例。此處可以自己定義 hadr.setFormatter(fmtr) fltr=logging.Filter('s') # 此處設置過濾器為s,則為s,s.s1均可通過 hadr.addFilter(fltr) loga.addHandler(hadr) loga.info('loga info') logb=logging.getLogger('s.s1') logb.getEffectiveLevel() logb.info('logb info') logc=logging.getLogger('s.s1') logc.getEffectiveLevel() logc.info('logc info')
結果如下

文件如下

import logging FORMAT="%(asctime)s %(thread)d %(message)s" logging.basicConfig(level=logging.ERROR,format=FORMAT,datefmt="%y-%m-%d %H:%M%S %z") #重新格式化傳值,定義日誌輸出到文件 root=logging.getLogger() print (root,id(root)) loga=logging.getLogger('s') # 模塊和模塊下的某個函數或類 print (loga,id(loga),id(loga.parent)) loga.setLevel(logging.INFO) print (loga.getEffectiveLevel()) #默認對應的是常量20 hadr=logging.FileHandler('/root/loga.txt') # 定義,不指定則是標準錯誤輸出 hadr.setLevel(logging.INFO) fmtr=logging.Formatter("%(asctime)s %(thread)d %(threadName)s %(message)s") #此處是一個實例。此處可以自己定義 hadr.setFormatter(fmtr) fltr=logging.Filter('a') # 此處設置過濾器為s,則為s,s.s1均可通過 hadr.addFilter(fltr) loga.addHandler(hadr) loga.info('loga info') logb=logging.getLogger('s.s1') logb.getEffectiveLevel() logb.info('logb info') logc=logging.getLogger('s.s1') logc.getEffectiveLevel() logc.info('logc info')
文件如下

消息loga,它的名字是's',因此過濾器名字設置為s,則s.s1,s.x和s都能通過,但其他的則不能通過,不設置過濾器名字,所有消息都能通過,設置為a,則和s,s.s1和s.s2無任何關係,因此其不能使用,實例對象只有一個,一旦同名,也是只有一個,線程的不安全不會影響logging,只會影響print。