Python強大的日誌模組logging

Huny發表於2021-01-03

前言

日誌是對於軟體執行所發生的事件的一種追蹤記錄方式。日常使用過程中對程式碼執行的錯誤和問題會進行檢視日誌來分析定位問題所在。平常編寫程式碼以及除錯也經常用到。通常的新手的做法是直接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')

image-20210103173713237

更改級別

我們將預設的級別改成最低階別,則會列印同級別以及高階別的日誌資訊

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')

image-20210103174637055

儲存日誌

只是列印到控制檯對於少量資訊倒是可控,但是資訊量大的時候就不方便查詢了。那麼我們需要將其儲存到檔案中。

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')

image-20210103204218029

上面我們在儲存的時候,同時還新增了其他描述資訊,比如執行時間,執行程式碼行數,日誌級別,列印訊息。當然方法遠不止這些,具體請看下錶

屬性名稱 格式 描述
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')
  

image-20210103215550860

當然也同樣能儲存到檔案,為了演示修改了檔名稱為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')

image-20210103220121427

日誌雙向輸出

既然之前的方案已經能達到相同的效果了,那麼後面的方法到底有什麼強大之處。我們來實現一個之前方法所不能達成的效果。

上面的日誌資訊雖然都儲存到了文字中,但是控制檯卻沒有了任何資訊,這對我們檢視日誌也不是很友好,我們希望的是既能在控制檯輸出,同時也能儲存到日誌檔案中。

我們新增兩個日誌記錄器物件,一個使用輸出到控制檯的功能一個使用儲存到檔案的功能,就可以實現同時執行輸出和儲存了。

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')

image-20210103212518964

image-20210103212532851

相關文章