Python強大的日誌模組logging
前言
日誌是對於軟體執行所發生的事件的一種追蹤記錄方式。日常使用過程中對程式碼執行的錯誤和問題會進行查看日誌來分析定位問題所在。平常編寫程式碼以及調試也經常用到。通常的新手的做法是直接print列印,但是列印的結果只在控制台顯示。今天我們學習一種高級的日誌列印和記錄模組logging。
logging提供了一系列的函數,它們是debug(), info(), warning(), error(), 和critical()。
他們的使用場景請看下錶
你想要執行的任務 | 此任務的最好的工具 |
---|---|
對於命令行或程式的應用,結果顯示在控制台。 | print() |
在對程式的普通操作發生時提交事件報告(比如:狀態監控和錯誤調查) | logging.info() 函數(當有診斷目的需要詳細輸出資訊時使用 logging.debug() 函數) |
提出一個警告資訊基於一個特殊的運行時事件 | warnings.warn()位於程式碼庫中,該事件是可以避免的,需要修改客戶端應用以消除告警logging.warning() 不需要修改客戶端應用,但是該事件還是需要引起關注 |
對一個特殊的運行時事件報告錯誤 | 引發異常 |
報告錯誤而不引發異常(如在長時間運行中的服務端進程的錯誤處理) | logging.error(), logging.exception() 或 logging.critical()分別適用於特定的錯誤及應用領域 |
日誌功能分別對各種事件和嚴重性都進行分級。
名稱 | 何時使用 | 等級 |
---|---|---|
DEBUG |
細節資訊,僅當診斷問題時適用。 | 10 |
INFO |
確認程式按預期運行 | 20 |
WARNING |
表明有已經或即將發生的意外(例如:磁碟空間不足)。程式仍按預期進行 | 30 |
ERROR |
由於嚴重的問題,程式的某些功能已經不能正常執行 | 40 |
CRITICAL |
嚴重的錯誤,表明程式已不能繼續執行 | 50 |
示例
簡單示例
因為是python自帶的所以無需安裝,默認的級別是WARNING,所以下面只顯示一條warning資訊。
import logging
logging.warning('this is warning')
logging.info('this is info')
更改級別
我們將默認的級別改成最低級別,則會列印同級別以及高級別的日誌資訊
import logging
logging.basicConfig(level=logging.DEBUG)
logging.debug('this is debug')
logging.info('this is info')
logging.warning('this is warning')
logging.error('this is error')
保存日誌
只是列印到控制台對於少量資訊倒是可控,但是資訊量大的時候就不方便查找了。那麼我們需要將其保存到文件中。
import logging
logging.basicConfig(level=logging.DEBUG,filename='log.log',format='%(asctime)s - %(lineno)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
logger.debug('this is debug')
logger.info('this is info')
logger.warning('this is warning')
logger.error('this is error')
上面我們在保存的時候,同時還添加了其他描述資訊,比如執行時間,執行程式碼行數,日誌級別,列印消息。當然方法遠不止這些,具體請看下錶
屬性名稱 | 格式 | 描述 |
---|---|---|
ARGS | 你不需要自己設置格式。 | 參數元組被合併到msg中以產生消息,或者其值被用於合併的詞典(當只有一個參數時,它是一個字典)。 |
asctime | %(asctime)類 | 創建時的可讀時間。默認情況下,這是’2003-07-08 16:49:45,896’的格式(逗號之後的數字是毫秒部分)。 |
created | %(created)的F | 創建的時間(由time.time()返回)。 |
exc_info | 你不需要自己設置格式。 | 異常元組(àla sys.exc_info)或,如果沒有發生異常,則為無。 |
filename | %(filename)類 | 路徑名的文件名部分。 |
funcName | %(funcName)類 | 包含日誌記錄調用的函數的名稱。 |
levelname | %(levelname)■ | 文本消息級別(’DEBUG’,’INFO’,’WARNING’,’ERROR’,’CRITICAL’)。 |
levelno | %(levelno)s | 消息的數字記錄級別(DEBUG,INFO,WARNING,ERROR,CRITICAL)。 |
lineno | %(lineno)d | 發出日誌記錄調用的源行號。 |
module | %(module)類 | 模組(文件名稱部分)。 |
msecs | %(msecs)d | 創建時的毫秒部分。 |
message | %(message)類 | 記錄的消息,計算為msg%args。這是在調用Formatter.format()時設置的。 |
msg | 你不需要自己設置格式。 | 在原始日誌記錄調用中傳遞的格式字元串。與args合併生成消息或任意對象(請參閱使用任意對象作為消息)。 |
name | %(name)類 | 用於記錄呼叫的記錄器的名稱。 |
pathname | %(filename)類 | 發出日誌記錄調用的源文件的完整路徑名。 |
process | %(process)d | 進程ID。 |
processName | %(processName)類 | 進程名稱。 |
relativeCreated | %(relativeCreated)d | 相對於載入日誌記錄模組的時間,LogRecord創建時的時間(以毫秒為單位)。 |
thread | %(thread)d | 執行緒ID。 |
threadName | %(threadName)類 | 執行緒名稱。 |
日誌輸出進階
首先了解以下進階的方法的說明:
StreamHandler
類位於核心 logging
包,它可將日誌記錄輸出發送到數據流例如 sys.stdout, sys.stderr 或任何文件類對象(或者更精確地說,任何支援 write()
和 flush()
方法的對象
FileHandler
類位於核心 logging
包,它可將日誌記錄輸出到磁碟文件中。 它從 StreamHandler
繼承了輸出功能。
我們需要通過調用 Logger
類(以下稱為 loggers , 記錄器)的實例來執行日誌記錄。
Logger
對象有三個常見的方法:
-
Logger.setLevel()
指定記錄器將處理的最低嚴重性日誌消息,其中 debug 是最低內置嚴重性級別, critical 是最高內置嚴重性級別。 例如,如果嚴重性級別為 INFO ,則記錄器將僅處理 INFO 、 WARNING 、 ERROR 和 CRITICAL 消息,並將忽略 DEBUG 消息。 -
Logger.addHandler()
和Logger.removeHandler()
從記錄器對象中添加和刪除處理程式對象。處理程式在以下內容中有更詳細的介紹 處理程式 。 -
Logger.addFilter()
和Logger.removeFilter()
可以添加或移除記錄器對象中的過濾器。 Filter 對象 包含更多的過濾器細節。
下面示例採用添加日誌記錄器對象輸出和上面一樣在控制台列印
import logging
logger = logging.getLogger(__name__)
logger.setLevel(level=logging.INFO)
handler = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(lineno)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.info('This is a log info')
logger.debug('Debugging')
logger.warning('Warning exists')
logger.info('Finish')
當然也同樣能保存到文件,為了演示修改了文件名稱為put.log
import logging
logger = logging.getLogger(__name__)
logger.setLevel(level=logging.INFO)
handler = logging.FileHandler('put.log')
formatter = logging.Formatter('%(asctime)s - %(lineno)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.info('This is a log info')
logger.debug('Debugging')
logger.warning('Warning exists')
logger.info('Finish')
日誌雙向輸出
既然之前的方案已經能達到相同的效果了,那麼後面的方法到底有什麼強大之處。我們來實現一個之前方法所不能達成的效果。
上面的日誌資訊雖然都保存到了文本中,但是控制台卻沒有了任何資訊,這對我們查看日誌也不是很友好,我們希望的是既能在控制台輸出,同時也能保存到日誌文件中。
我們添加兩個日誌記錄器對象,一個使用輸出到控制台的功能一個使用保存到文件的功能,就可以實現同時執行輸出和保存了。
import logging
logger = logging.getLogger(__name__)
logger.setLevel(level=logging.INFO)
handler1 = logging.FileHandler('output.log')
handler2 = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(lineno)s - %(levelname)s - %(message)s')
handler1.setFormatter(formatter)
handler2.setFormatter(formatter)
logger.addHandler(handler1)
logger.addHandler(handler2)
logger.info('This is a log info')
logger.debug('Debugging')
logger.warning('Warning exists')
logger.info('Finish')