Python 的 logging
模組提供強大的日誌記錄功能,但有效運用格式化和組態技巧才能充分發揮其效用。日期時間格式化是日誌記錄的基礎,利用 strftime
方法能客製化時間戳記格式。引數化日誌訊息則能提升日誌的可讀性和資訊量,使用 %s
等格式化字串搭配變數,讓日誌訊息更具體。logging
模組提供三種組態方式:使用 logging API 直接在程式碼中設定,適合簡單的日誌需求;使用 fileConfig
讀取設定檔,方便管理複雜組態;使用 dictConfig
以字典形式定義組態,兼具靈活性與可讀性。選擇哪種方式取決於專案規模和複雜度。理解這些技巧,才能建構更完善的日誌系統,提升程式碼除錯和系統監控效率。
日誌格式化
日誌格式化是日誌系統中的重要環節,它決定了日誌記錄的呈現方式。無論是用於除錯、監控還是稽核,日誌格式化都能幫助你提取有價值的資訊。本章將探討如何利用 Python 的 logging
模組來格式化日誌,並介紹一些常見的格式化技巧與方法。
日期與時間格式化
日期和時間是日誌中非常重要的一部分,因為它們提供了事件發生的時間戳記。Python 的 datetime
模組提供了豐富的日期和時間格式化功能,你可以根據需要進行自定義。
以下是一些常見的日期和時間格式碼:
%Y
:四位數年份(例如:2023)%m
:兩位數月份(例如:09)%d
:兩位數日期(例如:02)%H
:兩位數小時(24 小時制)(例如:13)%M
:兩位數分鐘(例如:28)%S
:兩位數秒鐘(例如:02)
這些格式碼可以組合使用來建立自定義的日期和時間格式。例如:
import datetime
# 取得當前時間
now = datetime.datetime.now()
# 自定義日期和時間格式
formatted_date = now.strftime("%Y-%m-%d %H:%M:%S")
print(formatted_date)
內容解密:
在這段程式碼中,我們使用了 datetime
模組來取得當前的日期和時間,然後使用 strftime
方法來將其轉換為指定的格式。這樣可以方便地將日期和時間記錄到日誌中,供後續分析和查詢使用。
使用引數
在記錄日誌時,你可能會需要傳遞引數以提高日誌的可讀性和資訊量。Python 的 logging
模組支援多種方式來進行字串替換,其中推薦使用 printf
風格的字串替換。
以下是一個示例:
name = "Mike"
print("Nice to meet you, %s" % name)
在這個示例中,我們使用了 %s
作為佔位符,並且在字串後面使用百分號 %
來插入變數 name
的值。
在 logging
模組中,我們可以這樣使用:
import logging
# 建立自定義日誌記錄器
logger = logging.getLogger(name="test")
logger.setLevel(logging.DEBUG)
# 建立檔案處理器
file_handler = logging.FileHandler("params.log")
# 新增處理器到日誌記錄器
logger.addHandler(file_handler)
# 新增格式化器
formatter = logging.Formatter()
file_handler.setFormatter(formatter)
name = "Mike"
logger.debug("Nice to meet you, %s", name)
內容解密:
這段程式碼展示瞭如何在 Python 中使用 logging
模組來進行字串替換。我們首先建立了一個名為 test
的日誌記錄器並設定其級別為 DEBUG
。然後,我們建立了一個檔案處理器並將其新增到日誌記錄器中。接著,我們建立了一個格式化器並設定到檔案處理器上。最後,我們使用字串替換來記錄一條包含變數 name
值的日誌訊息。
日誌組態
Python 的 logging
模組提供了三種主要方法來組態你的日誌系統。那麼什麼是日誌組態呢?簡單地說,日誌組態告訴 Python 的日誌記錄器以下幾點:
- 日誌應該存放在哪裡(處理器)
- 日誌訊息應該如何格式化(格式化器)
- 你希望記錄哪級別的日誌(DEBUG、INFO、CRITICAL 等)
- 有時還會有一些額外組態
本章將探討這三種不同的組態方法。
組態方法概覽
Python 提供了三種組態方式:
- 使用 logging API:透過程式碼直接組態。
- 使用 fileConfig:透過讀取組態檔案來組態。
- 使用 dictConfig:透過字典來組態。
這些方法為你提供了很大的靈活性。選擇哪種方法取決於你的經驗和專案型別。不必擔心第一次選擇不當,學習新事物時犯錯並改正是很正常的。
組態方式一:使用 logging API
透過程式碼直接組態日誌系統是最直接也最靈活的一種方式。以下是一個示例:
import logging
import time
def main():
"""
主入口函式
"""
logger = logging.getLogger("example_app")
logger.setLevel(logging.INFO)
# 建立檔案處理器
file_handler = logging.FileHandler("example.log")
formatter = logging.Formatter(
("%(asctime)s - %(name)s - %(levelname)s - %(message)s"))
file_handler.setFormatter(formatter)
# 建立控制檯處理器
stream_handler = logging.StreamHandler()
stream_formatter = logging.Formatter(
("%(asctime)s - %(filename)s - %(lineno)d - %(message)s"))
stream_handler.setFormatter(stream_formatter)
# 新增處理器到日誌記錄器
logger.addHandler(file_handler)
logger.addHandler(stream_handler)
logger.info("程式開始")
# 模擬一些工作
time.sleep(2)
logger.info("完成!")
if __name__ == "__main__":
main()
內容解密:
這段程式碼展示瞭如何透過程式碼直接組態日誌系統。首先,我們建立了一個名為 example_app
的日誌記錄器並設定其級別為 INFO
。然後,我們分別建立了一個檔案處理器和一個控制檯處理器,並為它們設定了不同的格式化器。最後,我們將這些處理器新增到日誌記錄器中,並在主函式中進行了一些模擬工作。
組態方式二:使用 fileConfig
透過讀取組態檔案來組態日誌系統也是一種常見的方式。這樣可以將組態資訊與程式碼分離開來,使得維護和修改更加方便。
假設你有一個名為 logging_config.ini
的組態檔案:
[loggers]
keys=root,example_app
[handlers]
keys=fileHandler,streamHandler
[formatters]
keys=defaultFormatter
[logger_root]
level=DEBUG
handlers=fileHandler,streamHandler
[logger_example_app]
level=INFO
handlers=
qualname=example_app
[handler_fileHandler]
class=FileHandler
args=('example.log',)
level=INFO
formatter=defaultFormatter
[handler_streamHandler]
class=StreamHandler
level=INFO
formatter=defaultFormatter
[formatter_defaultFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
然後在你的 Python 程式碼中讀取這個組態檔案:
import logging.config
logging.config.fileConfig('logging_config.ini')
logger = logging.getLogger('example_app')
logger.info('Program started')
# 模擬一些工作
time.sleep(2)
logger.info('Done!')
內容解密:
這段程式碼展示瞭如何透過讀取組態檔案來組態日誌系統。首先,我們使用 logging.config.fileConfig
函式讀取名為 logging_config.ini
的組態檔案。然後,我們取得名為 example_app
的日誌記錄器並進行一些模擬工作。
組態方式三:使用 dictConfig
透過字典來組態日誌系統也是一種靈活且強大的方式。以下是一個示例:
import logging.config
config = {
'version': 1,
'formatters': {
'default': {
'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
}
},
'handlers': {
'file': {
'class': 'logging.FileHandler',
'filename': 'example.log',
'formatter': 'default'
},
'stream': {
'class': 'logging.StreamHandler',
'formatter': 'default'
}
},
'loggers': {
'example_app': {
'handlers': ['file', 'stream'],
'level': 'INFO'
}
}
}
logging.config.dictConfig(config)
logger = logging.getLogger('example_app')
logger.info('Program started')
# 模擬一些工作
time.sleep(2)
logger.info('Done!')
內容解密:
這段程式碼展示瞭如何透過字典來組態日誌系統。首先,我們定義了一個名為 config
的字典,其中包含了各種組態資訊,包括處理器、格式化器和記錄器。然後,我們使用 logging.config.dictConfig
函式載入這些組態資訊。最後,我們取得名為 example_app
的日誌記錄器並進行一些模擬工作。
使用檔案組態Log記錄
在Python中,使用logging
模組來進行日誌記錄是非常普遍的做法。這章節將介紹如何使用檔案組態來管理日誌記錄,這樣可以將複雜的組態邏輯從程式碼中分離出來,使程式碼更加簡潔。
組態檔案結構
首先,我們需要了解組態檔案的結構。Python的logging
模組支援使用類別似Windows INI組態檔案的格式來進行組態。這種格式由configparser
模組來解析,並且它的結構非常簡單。
組態檔案的基本結構
- 區段(Section):區段用方括號(
[]
)來標示,例如[loggers]
。 - 鍵值對(Key-Value Pair):在區段內,每一行都是一個鍵值對,鍵和值之間用等號(
=
)分隔。
以下是一個簡單的組態檔案範例:
[loggers]
keys=root,example_app
[handlers]
keys=fileHandler, consoleHandler
[formatters]
keys=file_formatter, stream_formatter
[logger_root]
level=CRITICAL
handlers=consoleHandler
[logger_example_app]
level=INFO
handlers=fileHandler
qualname=example_app
[handler_consoleHandler]
class=StreamHandler
formatter=stream_formatter
args=(sys.stdout,)
[handler_fileHandler]
class=FileHandler
level=DEBUG
formatter=file_formatter
args=("config.log",)
[formatter_file_formatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=
[formatter_stream_formatter]
format=%(asctime)s - %(filename)s - %(lineno)s - %(message)s
datefmt=%a %d %b %Y
詳細解說
段落標題:區段說明
- loggers:定義所有可用的日誌器。
- handlers:定義所有可用的處理器。
- formatters:定義所有可用的格式化器。
次段落標題:日誌器組態
- logger_root:根日誌器,負責捕捉所有未分配到其他日誌器的日誌記錄。
- logger_example_app:自定義日誌器,可以用於特定應用程式或模組。
次段落標題:處理器組態
- handler_consoleHandler:將日誌記錄輸出到控制檯。
- handler_fileHandler:將日誌記錄寫入到檔案中。
次段落標題:格式化器組態
- formatter_file_formatter:定義寫入檔案時的日誌格式。
- formatter_stream_formatter:定義輸出到控制檯時的日誌格式。
使用檔案組態日誌
接下來,我們需要在Python程式碼中載入這個組態檔案並進行日誌記錄。以下是具體步驟:
- 建立一個名為
logging.conf
的組態檔案,內容如上所述。 - 建立一個Python檔案,例如
log_with_config.py
,並新增以下程式碼:
import logging
import logging.config
import time
def main():
logging.config.fileConfig("logging.conf")
logger = logging.getLogger("example_app")
logger.info("Program started")
time.sleep(3)
logger.info("Done!")
if __name__ == "__main__":
main()
#### 內容解密:
在此程式碼中,我們首先匯入了必要的模組,然後在main()
函式中使用logging.config.fileConfig()
方法來載入組態檔案。接著,我們取得了名為example_app
的日誌器並進行了一些基本的日誌記錄操作。
import logging # 匯入 logging 模組
import logging.config # 匯入 logging.config 模組以載入組態檔案
import time # 匯入 time 模組以使用 sleep 函式
def main():
logging.config.fileConfig("logging.conf") # 載入組態檔案
logger = logging.getLogger("example_app") # 取得名為 example_app 的日誌記錄器
logger.info("Program started") # 記錄一條資訊級別的日誌記錄
time.sleep(3) # 暫停3秒以模擬一些工作時間
logger.info("Done!") # 記錄另一條資訊級別的日誌記錄
if __name__ == "__main__":
main() # 執行主函式
當你執行這個程式碼時,你會看到類別似以下的輸出:
2023-10-05 Thu Oct 05 - log_with_config.py - 14 - Program started
2023-10-05 Thu Oct 05 - log_with_config.py - 17 - Done!
同時,你也會在組態檔案中指定的config.log
檔案中看到相應的日誌記錄。
視覺化此圖示展示了組態檔案與Python程式碼之間的關係
graph TD; A[config.log] --> B[FileHandler]; B --> C[FileFormatter]; D[Console] --> E[StreamHandler]; E --> F[StreamFormatter]; G[Logger: example_app] --> B; G --> E;