你可能不知道的python logging機制

2021-10-20 19:10:11 字數 3553 閱讀 3420

先來看幾個例子,先想一下執行結果,答案稍後揭曉:

例一:

mylogger = logging.getlogger("mylogger")

mylogger.info("mylogger info")

mylogger.warning("mylogger warning")

例二:

mylogger = logging.getlogger("mylogger")

mylogger.setlevel(logging.info)

mylogger.info("mylogger info")

mylogger.warning("mylogger warning")

例三:

logging.info("logging info")

logging.warning("logging warning")

先來一張官方流程圖鎮樓:

日誌級別是否不低於logger級別?是的話生成一條日誌(logrecord)

logger的filter是否過濾日誌?沒過濾的話依次呼叫logger中的handler

logger是否向上傳播(propagate=true)?是的話依次呼叫父logger的handler

其次是handler流程:

日誌級別是否不低於handler級別?是的話繼續

handler的filter是否過濾日誌?沒過濾的話就可以輸出日誌

logger 類和 handler 類都繼承自 filter 類,而不是直接在 logger 類和 handler 類裡各自維護乙個 filter 列表,目的是共享 filter 類的操作**

下面揭曉幾個例子的答案,如果你都答對了,下面就不需要再看啦:

例一

>>> mylogger = logging.getlogger("mylogger")

>>> mylogger.info("mylogger info")

>>> mylogger.warning("mylogger warning")

warning:mylogger:mylogger warning

例二

>>> mylogger = logging.getlogger("mylogger")

>>> mylogger.setlevel(logging.info)

>>> mylogger.info("mylogger info")

>>> mylogger.warning("mylogger warning")

mylogger warning

例三

>>> logging.info("logging info")

>>> logging.warning("logging warning")

warning:root:logging warning

和你想的一樣嗎?不一樣的話,接著往下看

從流程圖可以看到,從呼叫logging到日誌輸出,經過了兩道關卡,首先是logger類,然後是handler類

logging模組中管理logger例項的是manager類,採用了樹形結構,最上層是 root logger ,它是所有其他logger例項的最終父節點。在源**裡,root logger 的級別被設定成 warning 。

先來看一段命令列**:

例四

>>> mylogger = logging.getlogger("mylogger")

>>> mylogger.level

0>>> mylogger.info("hello world")

>>> mylogger.warning("hello world")

hello world

>>>

mylogger 的級別是 0,也就是 logging.notset,是 logging 裡級別最低的一檔,但為什麼呼叫 info 沒有輸出呢?

原因在於,logger 類有乙個「有效級別」(effectivelevel)的概念,logger 判斷級別是用 geteffectivelevel 函式,如果 logger 例項自身的 level 是 logging.notset,函式會迭代查詢父節點的 level,直到找到乙個非零的 level,稱為**「有效級別」**。在這個例子中,mylogger的父節點就是root,所以mylogger的有效級別是warning。

那麼只要設定下mylogger的level就可以正常輸出了:

>>> mylogger.setlevel(logging.info)

>>> mylogger.info("hello world")

>>>

等等,好像還是不對?別忘了,現在只是過了logger這關,還有一關沒過呢!

handler的級別控制很簡單,只看自己本身的level,如果建立的時候沒有指定level,預設是notset,也就是不做任何過濾。

每個logger建立的時候預設是沒有handler的,建立後可以新增多個handler。

回到例四的問題,mylogger沒有handler,為什麼 mylogger.warning(「hello world」)會輸出日誌呢?

lastresort是乙個handler例項,level是warning,輸出到sys.stderr。

顧名思義,這是個「最後手段」。當logger發現自身沒有任何handler的時候,會呼叫lastresort來處理,但僅限於warning級別及以上。

所以例四的答案是:mylogger呼叫了lastresort來處理,過濾了info級別的日誌,輸出了warning級別的日誌。

root logger是比較特殊的乙個,它是在模組裡建立好的,和其他logger相比,唯一區別是預設級別warning。可以用不帶引數的logging.getlogger()方法獲取,也遵循上述兩種級別控制。

如果你想用一行**記錄日誌,一般會這樣寫:

logging.info("so easy!")

logging.warning("may be not.")

這是logging模組提供的快捷方法,實質是呼叫了root logger,但和直接呼叫root logger不同之處在於,快捷方法如果發現root logger沒有handler,還會給root logger新增乙個預設的streamhandler,level是notset,輸出到sys.stderr。

沒有指定level的logger,只能輸出warning級別及以上的日誌

沒有新增handler的logger,只能輸出warning級別及以上的日誌

直接呼叫logging.info, logging.warning函式,只能輸出warning級別及以上的日誌

回到開頭三個例子,現在你明白了嗎?

你可能不知道的東西

元素可以分為塊級元素,行內元素以及行內塊級元素。行內元素的margin或者padding只有margin left和margin right以及padding left和padding right有效果,margin top margin bottom padding top padding bot...

你可能不知道的const

眾所周知,使用 const 宣告的變數必須同時初始化為某個值。一經宣告,在其生命週期的任何時候都不能再重新賦予新值 const a syntaxerror 常量宣告時沒有初始化 const b 3 console.log b 3 b 4 typeerror 給常量賦值const 宣告只應用到頂級原語...

你可能不知道的viewport

前幾天偶然看到乙個pc端網頁,發現用手機開啟竟然同比縮放了,作為乙個前端從業者,我自然想要弄清它到底是怎麼縮放的。之後查了它的meta資訊,css和js,發現沒有任何相容手機端的 那它到底是怎麼縮放的呢?百思不得其解,最後無意中看別人說viewport的預設值是980px,才知道原來是viewpor...