Python 的 logging 模組是開發過程中不可或缺的工具,能有效追蹤程式執行狀態、捕捉錯誤資訊,進而提升程式碼的健壯性。本文從日誌級別的設定開始,逐步介紹如何使用 logging 模組,包含 basicConfigFileHandlerStreamHandler 等常用功能,並搭配程式碼範例說明如何建立自定義 logger 和多重 handler,讓讀者能快速上手,建構更完善的日誌系統。接著,文章進一步探討如何使用多個日誌處理器,例如同時將日誌訊息輸出到檔案和終端機,以滿足不同場景的需求。同時也提及了日誌處理器的執行緒安全特性,確保在多執行緒環境下也能穩定執行。最後,文章詳細介紹了日誌格式化的重要性,以及如何使用 Formatter 物件自定義日誌訊息的輸出格式,包含時間戳、日誌級別、程式碼位置等關鍵資訊,使日誌更具可讀性和分析價值,有效協助開發者快速定位問題。

Python Logging 入門:從基本到進階

理解 Python 的 Log Level

Python 的 logging 模組提供了豐富的功能來幫助開發者追蹤和除錯應用程式。透過適當地使用 log level,你可以控制哪些訊息會被記錄下來。以下是常見的 log level:

  1. DEBUG:詳細的訊息,通常只在開發階段使用。
  2. INFO:確認程式執行過程中的事件。
  3. WARNING:表示可能會有問題的情況,但不影響程式執行。
  4. ERROR:表示出現錯誤的情況,但程式仍能執行。
  5. CRITICAL:非常嚴重的錯誤,可能導致程式無法繼續執行。

以下是一個簡單的範例,展示如何使用不同的 log level:

import logging

logging.basicConfig(level=logging.DEBUG)

logging.debug("這是一個 DEBUG 訊息")
logging.info("這是一個 INFO 訊息")
logging.warning("這是一個 WARNING 訊息")
logging.error("這是一個 ERROR 訊息")
logging.critical("這是一個 CRITICAL 訊息")

內容解密:

  • import logging:匯入 Python 的 logging 模組。
  • logging.basicConfig(level=logging.DEBUG):設定基本組態,並指定 log level 為 DEBUG。這意味著所有等級的訊息都會被記錄。
  • logging.debug("這是一個 DEBUG 訊息"):記錄一個 DEBUG 等級的訊息。
  • logging.info("這是一個 INFO 訊息"):記錄一個 INFO 等級的訊息。
  • logging.warning("這是一個 WARNING 訊息"):記錄一個 WARNING 等級的訊息。
  • logging.error("這是一個 ERROR 訊息"):記錄一個 ERROR 等級的訊息。
  • logging.critical("這是一個 CRITICAL 訊息"):記錄一個 CRITICAL 等級的訊息。

使用 Log Handlers 控制日誌輸出

Log handlers 是 Python logging 模組中的關鍵元件,它們決定日誌訊息應該被傳送到哪裡。以下是一些常見的 log handlers:

  1. StreamHandler:將日誌訊息輸出到標準輸出(如終端機)。
  2. FileHandler:將日誌訊息寫入檔案。
  3. RotatingFileHandler:根據檔案大小或時間間隔自動旋轉日誌檔案。
  4. TimedRotatingFileHandler:根據時間間隔自動旋轉日誌檔案。

基本組態與 Handler 的使用

以下是如何使用 basicConfigFileHandler 的範例:

import logging

# 基本組態,將日誌寫入 test.log
logging.basicConfig(filename="test.log", level=logging.DEBUG)

# 記錄一些日誌
logging.debug("Hello debug")
logging.info("Hello info")

內容解密:

  • import logging:匯入 Python 的 logging 模組。
  • logging.basicConfig(filename="test.log", level=logging.DEBUG):設定基本組態,將日誌寫入名為 test.log 的檔案,並設定 log level 為 DEBUG。
  • logging.debug("Hello debug"):記錄一個 DEBUG 等級的訊息。
  • logging.info("Hello info"):記錄一個 INFO 等級的訊息。

自定義 Logger 與多重 Handlers

透過自定義 logger,你可以更靈活地控制日誌的輸出。以下是如何建立自定義 logger 和使用多重 handlers 的範例:

import logging

# 建立自定義 logger
logger = logging.getLogger(name="test")
logger.setLevel(logging.DEBUG)

# 建立 FileHandler 和 StreamHandler
file_handler = logging.FileHandler("test_handler.log")
stream_handler = logging.StreamHandler()

# 新增 handlers 到 logger
logger.addHandler(file_handler)
logger.addHandler(stream_handler)

# 記錄一些日誌
logger.debug("Hello debug")
logger.info("Hello info")

內容解密:

  • logger = logging.getLogger(name="test"):建立一個名為 “test” 的自定義 logger。
  • logger.setLevel(logging.DEBUG):設定 logger 的 log level 為 DEBUG。
  • file_handler = logging.FileHandler("test_handler.log"):建立一個 FileHandler,將日誌寫入名為 test_handler.log 的檔案。
  • stream_handler = logging.StreamHandler():建立一個 StreamHandler,將日誌輸出到標準輸出(如終端機)。
  • logger.addHandler(file_handler):將 FileHandler 新增到 logger 中。
  • logger.addHandler(stream_handler):將 StreamHandler 新增到 logger 中。
  • logger.debug("Hello debug"):記錄一個 DEBUG 等級的訊息。
  • logger.info("Hello info"):記錄一個 INFO 等級的訊息。

日誌處理器的多樣應用

在Python中,日誌(logging)是一個強大且靈活的工具,能夠幫助我們追蹤程式的執行情況和錯誤。日誌處理器(handler)是日誌系統中的核心元件,負責將日誌記錄傳送到不同的目的地。本文將探討如何使用多個日誌處理器來提升日誌系統的靈活性和實用性。

多個日誌處理器的應用

在實際開發中,我們常常需要將日誌記錄同時傳送到多個目的地,例如檔案、控制檯以及網路伺服器。這樣可以方便我們在不同環境下檢視和分析日誌資訊。以下是一個簡單的範例,展示如何使用兩個日誌處理器來將日誌記錄同時寫入檔案和控制檯。

範例程式碼

# two_handlers.py

import logging

# 建立自訂日誌記錄器
logger = logging.getLogger(name="test")
logger.setLevel(logging.DEBUG)

# 建立檔案處理器和控制檯處理器
file_handler = logging.FileHandler("two_handlers.log")
stream_handler = logging.StreamHandler()

# 將處理器新增到日誌記錄器
logger.addHandler(file_handler)
logger.addHandler(stream_handler)

# 測試日誌記錄
logger.debug("Hello debug")
logger.info("Hello info")

內容解密:

  1. 建立自訂日誌記錄器:首先,我們使用 logging.getLogger 方法建立了一個名為 “test” 的自訂日誌記錄器。然後,設定其日誌級別為 DEBUG,這意味著它會記錄所有級別的日誌資訊。

  2. 建立處理器:接著,我們建立了兩個處理器:FileHandlerStreamHandlerFileHandler 用於將日誌記錄寫入檔案 “two_handlers.log”,而 StreamHandler 用於將日誌記錄輸出到控制檯。

  3. 新增處理器:將這兩個處理器新增到自訂日誌記錄器中,這樣當我們呼叫 logger.debuglogger.info 時,日誌記錄會同時寫入檔案和控制檯。

  4. 測試日誌記錄:最後,我們測試了兩種不同級別的日誌記錄,分別是 DEBUGINFO

日誌處理器的其他特性

除了基本的功能外,Python 的日誌處理器還提供了一些高階特性,能夠進一步提升其靈活性和實用性。

執行緒安全

Python 的日誌處理器是執行緒安全的,這意味著它們可以在多執行緒環境下安全地使用。以下是兩個與執行緒相關的方法:

  • acquire():取得執行緒鎖。
  • release():釋放執行緒鎖。

這些方法可以幫助我們在多執行緒環境下避免資源競爭問題。

日誌格式化

我們可以為每個處理器設定不同的格式化方式(formatter),以便更好地展示日誌資訊。例如,我們可以在檔案中使用詳細的格式化方式,而在控制檯上使用簡短的格式化方式。

日誌過濾

我們還可以為每個處理器設定不同的過濾條件(filter),以便只顯示感興趣的部分日誌資訊。例如,我們可以只在控制檯上顯示 ERRORCRITICAL 級別的日誌資訊。

Python 日誌格式化

除了使用不同的處理器來管理和儲存程式中的重要資訊之外,Python 的 日誌系統還提供了另一種將資訊以可讀形式呈現出來的強大功能——格式化(Formatters)。透過對不同資訊型別進行適當格式化處理 ,我們可以使我們最終得到的訊息更具可讀性和實用性。本文將探討什麼是 Python 日誌格式化以及如何有效地應用它。

日誌格式化基礎

當你檢視程式執行過程中產生的一些基本訊息時 ,你可能會覺得這些訊息並沒有提供太多有用資訊 ,這並不是你所期望看到的一些詳細輸出 。為瞭解決這個問題 ,Python 提供了一種靈活且強大的方法來對這些訊息進行處理 :格式化(Formatter)。

常見需要新增到訊息中的附加資訊

為了更好地理解和分析應用執行時發生的一些問題 ,你通常需要新增一些附加資訊來輔助你進行排查和除錯 。以下是一些常見需要新增到訊息中的附加資訊:

  • 時間戳:記錄訊息產生時的時間點。
  • 行號:記錄訊息產生時所在程式碼行。
  • 日誌級別:記錄訊息型別(例如:DEBUG、INFO、WARNING、ERROR)。
  • 檔名:記錄產生該訊息所在檔名。
  • 模組名:記錄產生該訊息所在模組名。

除了以上這些資訊之外 ,你還可能會有其他需要根據具體業務場景增加一些其他附加資訊 。

內容解密:

  1. 時間戳:時間戳可以幫助我們追蹤問題發生時刻 ,從而更好地進行問題定位。
  2. 行號:記錄訊息產生時所在程式碼行 ,這對於快速定位問題非常有幫助 。
  3. 日誌級別:不同級別表示不同嚴重程度 的問題 ,我們可以根據其嚴重程度做出相應處理。
  4. 檔名:記錄產生該訊息所在檔名 ,這對於跨多個檔案進行排查非常有幫助 。
  5. 模組名:記錄產生該訊息所在模組名 ,這對於跨多個模組進行排查非常有幫助 。

建立一個 Logging Formatter 物件

當你需要建立一個新的 Python 日誌物件時 ,你需要三個關鍵元件 :Logger 物件 、Handler 物件 和 Formatter 物件 。 在之前章節中 ,我們已經討論過前兩者 。現在讓我們來深入瞭解一下 Formatter 物件 。

根據 Python 的官方檔案定義 ,一個 Formatter 物件通常如下所示:

logging.Formatter(fmt=None,
                  datefmt=None,
                  style='%',
                  validate=True,
                  defaults=None)

內容解密:

  1. fmt:這是一個格式字串 ,它決定了輸出訊息應該如何顯示 。你將學習如何使用該字串來建構出符合需求 的輸出格式 。
  2. datefmt:這是一個日期/時間字串格式 。你可以使用它來定義日期/時間部分 在輸出訊息中的顯示方式 。
  3. style:這決定了如何合併 fmt 引數中指定 的字串 。目前支援三種風格:’%’(printf-style) 、’{’ (str.format() style) 和 ‘$’(string.Template-style) 。
  4. validate:當設定為 True 時(預設值),如果 fmt 引數 和 style 引數 不正確會丟擲 ValueError 異常 。
  5. defaults:這是一個 dict[str, Any]型別 的引數 , 用於定義一些自定義欄位 值 。

應用 Logging Formatter 物件範例

接下來展示如何實際應用 Logging Formatter 物件來實作對所有透過 logger 物件輸出 的 log 訊息進行完整格式化處理 。

# hello_formatter.py

import logging

# 建立自訂 Logger 物件
logger = logging.getLogger(name="test")
logger.setLevel(logging.DEBUG)

# 建立 FileHandler 物件
file_handler = logging.FileHandler("test_formatter.log")

# 將 FileHandler 新增到 Logger 物件中
logger.addHandler(file_handler)

# 建立 Formatter 物件
formatter = logging.Formatter(
    '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)

# 將 Formatter 新增到 FileHandler 物件中
file_handler.setFormatter(formatter)

# 測試 Logger 物件
logger.debug("This is a debug message")
logger.info("This is an info message")

內容解密:

  1. 建立自訂 Logger 物件:首先建立一個命名為 “test” 的 Logger 物件並設定其等級為 DEBUG 。

  2. 建立 FileHandler 物件:接著建立一個 FileHandler 物件並設定其輸出位置為 “test_formatter.log” 檔案 。

  3. 設定 LogFormatter物件 : 然後建立一個 Formatter 物件並定義其輸出格式為:“%(asctime)s - %(name)s - %(levelname)s - %(message)s” 。這樣每次輸出時都會包含時間戳、Logger 名稱、等級以及具體訊息內容 。

  4. 將 Formatter 新增到 FileHandler 中 : 最後再將該 Formatter 物件新增到之前建立好的 FileHandler 中 。

  5. 測試 Logger 物件 : 輸出一些測試資訊來驗證Formatter 是否正常工作 。

日誌格式化與引數設定

除了基本組態之外 ,Python 的 Logger 還支援一些更高階 特性 。比如透過使用引數設定 (引數名稱):你可以為各個 logger 物件 新增額外引數並根據這些引數來動態調整輸出內容 。不過本章暫時不討論這個主題 。如果你有興趣瞭解詳細內容 ,請參考後續章節 。