Python日誌記錄中新增自定義屬性

banq發表於2024-05-17

日誌記錄對於任何軟體系統都是必不可少的。使用日誌,您可以解決各種問題,包括除錯應用程式錯誤、安全缺陷、系統緩慢等。在本文中,我們將討論如何使用自定義屬性有效地使用Python日誌記錄。

Python 日誌記錄
在我們深入研究之前,我想用一個例子簡單地解釋一下基本的 Python 日誌模組。

#!/opt/bb/bin/python3.7

import logging
import sys

root = logging.getLogger()
root.setLevel(logging.DEBUG)
std_out_logger = logging.StreamHandler(sys.stdout)
std_out_logger.setLevel(logging.INFO)
std_out_formatter = logging.Formatter(<font>"%(levelname)s - %(asctime)s  %(message)s")
std_out_logger.setFormatter(std_out_formatter)
root.addHandler(std_out_logger)

logging.info(
"I love DDD!")

上面的示例在執行時列印以下內容:

INFO - 2024-03-09 19:49:07,734  I love DDD!


在上面的示例中,我們正在建立root記錄器和日誌訊息的記錄格式。在第 6 行,logging.getLogger()如果已建立,則返回記錄器;如果沒有,它會上升到層次結構的上一級並返回父記錄器。我們定義自己的StreamHandler來在控制檯列印日誌訊息。

每當我們記錄訊息時,都必須記錄.LogRecord在第 10 行,我們定義了基本格式,包括級別名稱、字串格式的時間以及實際訊息本身。由此建立的處理程式設定在根記錄器級別。

我們可以使用LogRecord庫中的任何預定義日誌屬性名稱和格式。但是,假設您想要列印一些附加屬性contextId,例如自定義日誌記錄介面卡來救援。

記錄介面卡

class MyLoggingAdapter(logging.LoggerAdapter):

    def __init__(self, logger):
        logging.LoggerAdapter.__init__(self, logger=logger, extra={})

    def process(self, msg, kwargs):
        return msg, kwargs

我們建立自己的版本Logging Adapter並將“額外”引數作為格式化程式的字典傳遞。

上下文ID過濾器

import contextvars

class ContextIdFilter(logging.Filter):

    context_id = contextvars.ContextVar('context_id', default='')

    def filter(self, record):
        # add a new UUID to the context.
        req_id = str(uuid.uuid4())
        if not self.context_id.get():
            self.context_id.set(req_id)
        record.context_id = self.context_id.get()
        return True


我們建立自己的過濾器來擴充套件logging過濾器,該過濾器返回True是否應記錄指定的日誌記錄。我們只需將我們的引數新增到日誌記錄中並return True始終,從而將我們的唯一引數新增id到記錄中。在上面的示例中,id為每個新上下文生成一個唯一的上下文。對於現有上下文,我們返回contextId已從contextVars.

自定義記錄器

import logging

root = logging.getLogger()
root.setLevel(logging.DEBUG)
std_out_logger = logging.StreamHandler(sys.stdout)
std_out_logger.setLevel(logging.INFO)
std_out_formatter = logging.Formatter(<font>"%(levelname)s - %(asctime)s ContextId:%(context_id)s  %(message)s")
std_out_logger.setFormatter(std_out_formatter)
root.addHandler(std_out_logger)
root.addFilter(ContextIdFilter())

adapter = MyLoggingAdapter(root)

adapter.info(
"I love DDD!")
adapter.info(
"this is my custom logger")
adapter.info(
"Exiting the application")


現在讓我們將其放在記錄器檔案中。將contextId過濾器新增到root.請注意,我們在需要記錄訊息的地方使用我們自己的介面卡來代替記錄。

執行上面的程式碼會列印以下訊息:

INFO - 2024-04-20 23:54:59,839 ContextId:c10af4e9-6ea4-4cdf-9743-ea24d0febab6 I love DDD!
INFO - 2024-04-20 23:54:59,842 ContextId:c10af4e9-6ea4-4cdf-9743-ea24d0febab6 this is my custom logger
INFO - 2024-04-20 23:54:59,843 ContextId:c10af4e9-6ea4-4cdf-9743-ea24d0febab6 Exiting the application


透過設定root.propagate = False,記錄到此記錄器的事件將被傳遞到更高階別的日誌記錄(也稱為父日誌記錄類)的處理程式。

結論
Python 不提供內建選項來在日誌記錄中新增自定義引數。相反,我們在 Python 根記錄器之上建立一個包裝記錄器並列印我們的自定義引數。這在除錯特定於請求的問題時會很有幫助。

相關文章