為什么Pycharm輸出的日志全部是紅色!
在上一篇文章1萬字詳解 python logging日志模塊 中,深入淺出的講解了日志的基本原理與用法。但還有一些內容并沒有涉及到,所以這篇文章作為上一篇文章的補充。
希望這兩篇文章能幫助你完全理解日志模塊的使用,在項目中對日志的運用游刃有余。上一篇還沒看的建議先閱讀上一篇
1、為什么子記錄器不需要設置日志等級也可以輸出?
如果未在記錄器上顯式設置級別,則使用其父記錄器的級別作為其有效級別。如果父記錄器也沒有設置級別,則依此類推,搜索父級的父級,直到找到明確設置了級別的記錄器。根記錄器默認下為 WARNING 級別
- import logging
- parent = logging.getLogger("parent")
- parent.setLevel(logging.INFO)
- parent.addHandler(logging.StreamHandler())
- child = logging.getLogger("parent.child")
- child.info("msg")
輸出
- msg
這里我沒有給child設置日志等級,他會從父記錄器查找日志級別,所以child也可以輸出info級別的日志。關于記錄器的繼承關系可以參考第一篇文章
2、為什么有時候日志會輸出兩次?
看下面例子:
- import logging
- # 初始化日志,并設置日志級別(為root設置為DEBUG級別,關聯StreamHandler,設置BASIC_FORMAT格式)
- logging.basicConfig(level=logging.DEBUG)
- # 定義root記錄器
- root = logging.getLogger()
- # 定義child記錄器
- child = logging.getLogger("child")
- console_handler = logging.StreamHandler()
- # 給child綁定處理器
- child.addHandler(console_handler)
- # 記錄一條info日志
- child.info("child info")
輸出
- child info
- INFO:child:child info
代碼中明明只記錄了一次日志,卻輸出了兩次,而且兩次的日志格式不一樣。這是因為 child 這個記錄器添加了一個叫console_handler的處理器, 而root根記錄器默認也帶有自己的處理器(也是StreamHandler實例)
- print(root.handlers) # [<StreamHandler <stderr> (NOTSET)>]
根據python中日志模塊的處理機制,子記錄器記錄的消息會自動傳播給父級記錄器的關聯的處理器。所以在這個例子中,child記錄的消息除了會發給自己的handler外,還是傳播給root記錄器的handler,因此最終輸出了兩次,流程圖如下
logging-flow.png
如果不希望子記錄器記錄的消息傳播給父級記錄器,可以設置記錄器的屬性propagate為False,關閉傳播。
- child.propagate = False
如此一來,最終輸出到終端的日志就只有child自己的處理器輸出的記錄
- child info
配置處理器的最佳實踐是給頂級記錄器配置處理器,再根據需要創建子記錄器, 因為記錄最終都會傳播給父記錄器
- import logging
- parent = logging.getLogger("parent")
- parent.setLevel(logging.DEBUG)
- parent.addHandler(logging.StreamHandler())
- # 不需要給子記錄器單獨配置handler
- child = logging.getLogger("parent.child")
- child.info("msg")
對于日志的流程處理,python官方文檔畫了一張更為細致的流程圖,可以參考
logging_flow.png
第一次看估計有點暈,但先看我畫的這張圖再來看這張圖,你就能懂了,為了簡化我省去了過濾器以及不斷循環查找父級記錄器的這個流程。
3、 為什么我的pycharm中輸出的日志是紅色?
不知道你的pycharm輸出的日志不管是info信息還是error信息,反正都是紅色,一看以為整屏都是錯誤。
把下面代碼放在Pycharm運行看效果:
- import logging
- logging.basicConfig(level=logging.DEBUG)
- logging.info("hello")
這是因為使用root記錄器記錄日志時,默認配置的handler是一個StreamHandler。
我們打開StreamHandler的源碼
- class StreamHandler(Handler):
- """
- A handler class which writes logging records, appropriately formatted,
- to a stream. Note that this class does not close the stream, as
- sys.stdout or sys.stderr may be used.
- """
- terminator = '\n'
- def __init__(self, stream=None):
- """
- Initialize the handler.
- If stream is not specified, sys.stderr is used.
- """
- Handler.__init__(self)
- if stream is None:
- stream = sys.stderr
- self.stream = stream
初始化這個Handler時,會接收一個stream的參數,如果不傳,默認就使用的系統標準錯誤流(sys.stderr)輸出,pycharm對錯誤流輸出的字體樣式做了紅色渲染,如果換成 sys.stdout 輸出的就不再紅色了。
- import logging
- import sys
- handler = logging.StreamHandler(stream=sys.stdout)
- logging.basicConfig(level=logging.DEBUG, handlers=[handler])
- # 或者指定stream參數
- # logging.basicConfig(level=logging.DEBUG, stream=sys.stdout)
- logging.info("hello")
4、怎么生成以日期時間命名的日志?
實際應用中,我們會對日志進行歸檔存儲,每天生成一份日志,如果哪天出了問題,也方便定位,直接找到當天的日志文件就可以分析。我們只需要給logger添加一個TimedRotatingFileHandler處理器就行。
- file_handler = TimedRotatingFileHandler(‘'logs/api.log'),
- when="D", interval=1, backupCount=10,
- encoding="UTF-8", delay=False, utc=True)
- formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
- file_handler.setFormatter(formatter)
- file_handler.setLevel(logging.INFO)
- logger.addHandler(file_handler)
5、為什么我日志配置后不生效?
可能跟程序的加載順序有關,看個例子
- import logging
- logger = logging.getLogger()
- handler = logging.StreamHandler()
- logger.addHandler(handler)
- logger.info("hello1")
- logger.setLevel(logging.INFO)
- logger.info("hello2")
像上面的代碼最后只輸出了hello2,不過實際場景中,代碼沒這么簡單,通常是在a模塊中的某個函數中初始化日志框架配置, 在b模塊外層創建了名字叫 logger_b的記錄器,然后在a中導入b模塊時,這時候日志配置還沒初始化,最后導致logger_b的配置就成了默認配置。所以有可能出現日志不生效的情況。
因此最佳實踐是能盡早初始化日志配置就盡早提前。