目錄
- 什麼是 Logging?
- 為什麼需要 Logging?
- Logging 的五個層級
- 基礎使用方式
- 進階配置:Logger、Handler、Formatter
- 實戰案例:文件處理日誌系統
- 最佳實踐與注意事項
什麼是 Logging?
Logging 是 Python 內建的日誌記錄模組,用於追蹤程式執行過程中發生的事件。它比單純使用 print()
更強大,提供了分級記錄、格式化輸出、多目標輸出等功能。
為什麼需要 Logging?
相較於 print()
,Logging 有以下優勢:
- 分級管理:可以區分 DEBUG、INFO、WARNING、ERROR、CRITICAL 等不同嚴重程度
- 靈活輸出:可同時輸出到檔案、控制台、網路等多個目標
- 生產環境友善:可以在不修改程式碼的情況下調整日誌層級
- 格式化:統一的時間戳記、函數名稱、行號等資訊
- 效能:可以輕鬆關閉低層級日誌,提升效能
Logging 的五個層級
從低到高的嚴重程度:

基礎使用方式
1. 最簡單的 Logging
import logging
# 基本配置
logging.basicConfig(level=logging.INFO)
# 記錄不同層級的訊息
logging.debug("這是除錯訊息") # 不會顯示(層級太低)
logging.info("這是一般訊息") # 會顯示
logging.warning("這是警告訊息") # 會顯示
logging.error("這是錯誤訊息") # 會顯示
logging.critical("這是嚴重錯誤") # 會顯示
輸出:

2. 設定輸出格式
import logging
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s | %(levelname)s | %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
logging.info("程式啟動")
logging.debug("變數 x = 10")
輸出:

在 Jupyter Notebook 中,
logging.basicConfig 只會在「第一次」呼叫時生效,
之後的 cell 可能不會重新設定 handler
force=True 會強制重新設定 logging
3. 輸出到檔案
# %%
# %%
import logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s | %(levelname)s | %(message)s',
filename=r'D:\Temp\app.log',
filemode='w', # 'w' 覆蓋,'a' 附加
encoding='utf-8',force=True
)
logging.info("這條訊息會寫入檔案")
進階配置:Logger、Handler、Formatter
Logging 系統由三個核心組件構成:
- Logger:日誌記錄器,負責發出日誌訊息
- Handler:處理器,決定日誌輸出到哪裡(檔案、控制台等)
- Formatter:格式器,定義日誌的輸出格式
架構圖解
Logger → Handler → Formatter → 輸出目標
(可有多個)
完整範例:同時輸出到檔案和控制台
import logging
# 創建 Logger
logger = logging.getLogger('my_app')
logger.setLevel(logging.DEBUG)
# 創建檔案 Handler
file_handler = logging.FileHandler('app.log', encoding='utf-8')
file_handler.setLevel(logging.INFO)
# 創建控制台 Handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)
# 創建 Formatter
formatter = logging.Formatter(
'%(asctime)s | %(name)s | %(levelname)s | %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
# 將 Formatter 設定給 Handler
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)
# 將 Handler 加入 Logger
logger.addHandler(file_handler)
logger.addHandler(console_handler)
# 使用 Logger
logger.debug("這會顯示在控制台,但不會寫入檔案")
logger.info("這會同時顯示在控制台和檔案")
logger.error("發生錯誤!")
輸出:

格式化選項說明
常用的格式化參數:
formatter = logging.Formatter(
'%(asctime)s | ' # 時間
'%(name)s | ' # Logger 名稱
'%(levelname)s | ' # 層級名稱
'%(funcName)s | ' # 函數名稱
'%(lineno)d | ' # 行號
'%(message)s' # 訊息內容
)
動態獲取調用函數名稱
import logging
import inspect
logger = logging.getLogger('demo')
logger.setLevel(logging.INFO)
console_handler = logging.StreamHandler()
formatter = logging.Formatter('%(levelname)s | %(funcName)s | %(message)s')
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)
def process_data():
# 自動記錄函數名稱
logger.info("開始處理資料")
# 手動獲取調用者的函數名稱
frame = inspect.currentframe()
caller_name = frame.f_code.co_name
logger.info(f"當前函數: {caller_name}")
def main():
logger.info("程式啟動")
process_data()
main()
輸出:

實戰案例:文件處理日誌系統
以下是一個完整的文件處理日誌系統,
展示如何在實際專案中使用 Logging:
完整程式碼
import logging
import inspect
from datetime import datetime
from pathlib import Path
class DocumentLogger:
"""文件處理日誌記錄器"""
def __init__(self, output_file_path=None):
"""
初始化日誌記錄器
Args:
output_file_path: 輸出檔案路徑,log 檔案會在同目錄下
"""
self.logger = None
self.log_path = None
self.start_time = datetime.now()
if output_file_path:
self.setup_logger(output_file_path)
def setup_logger(self, output_file_path):
"""設置日誌記錄器"""
# 根據輸出檔案路徑生成 log 路徑
file_path = Path(output_file_path)
self.log_path = file_path.with_suffix('.log')
# 創建 logger
self.logger = logging.getLogger('DocumentProcessor')
self.logger.setLevel(logging.INFO)
# 清除已存在的 handlers(避免重複)
for handler in self.logger.handlers[:]:
self.logger.removeHandler(handler)
# 創建檔案 handler
file_handler = logging.FileHandler(
self.log_path,
mode='w',
encoding='utf-8'
)
file_handler.setLevel(logging.INFO)
# 創建控制台 handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
# 創建格式器
formatter = logging.Formatter(
'%(asctime)s | %(levelname)s | %(funcName)s | %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)
# 添加 handlers
self.logger.addHandler(file_handler)
self.logger.addHandler(console_handler)
# 記錄開始訊息
self.logger.info("=" * 80)
self.logger.info("文件處理開始")
self.logger.info(f"輸出檔案: {output_file_path}")
self.logger.info(f"日誌檔案: {self.log_path}")
self.logger.info("=" * 80)
def log_action(self, action_type, details, status="成功"):
"""
記錄操作日誌
Args:
action_type: 動作類型
details: 詳細訊息
status: 狀態(成功/失敗)
"""
if not self.logger:
return
status_icon = "✅" if status == "成功" else "❌"
message = f"{status_icon} {action_type} | {details} | 狀態: {status}"
if status == "成功":
self.logger.info(message)
else:
self.logger.error(message)
def log_file_operation(self, operation, file_name, details=""):
"""記錄檔案操作"""
self.log_action(
"檔案操作",
f"{operation} '{file_name}' {details}"
)
def log_data_processing(self, data_type, count, details=""):
"""記錄資料處理"""
self.log_action(
"資料處理",
f"處理 {count} 筆 {data_type} {details}"
)
def log_error(self, action_type, error_message, details=""):
"""記錄錯誤"""
self.log_action(
action_type,
f"{details} | 錯誤: {error_message}",
status="失敗"
)
def log_summary(self):
"""記錄處理摘要"""
if not self.logger:
return
end_time = datetime.now()
duration = end_time - self.start_time
self.logger.info("=" * 80)
self.logger.info("文件處理完成")
self.logger.info(f"處理時間: {duration}")
self.logger.info(f"結束時間: {end_time.strftime('%Y-%m-%d %H:%M:%S')}")
self.logger.info("=" * 80)
# 使用範例
if __name__ == "__main__":
# 創建日誌記錄器
doc_logger = DocumentLogger("output/report.docx")
# 模擬檔案處理流程
doc_logger.log_file_operation("開啟", "template.docx")
doc_logger.log_data_processing("使用者資料", 150, "從資料庫讀取")
doc_logger.log_file_operation("插入", "chart.png", "在第3頁")
# 模擬錯誤
try:
# 假設這裡發生錯誤
raise ValueError("找不到指定的圖片檔案")
except Exception as e:
doc_logger.log_error("圖片處理", str(e), "嘗試插入 logo.png")
# 繼續處理
doc_logger.log_file_operation("儲存", "report.docx")
# 記錄摘要
doc_logger.log_summary()
執行結果範例
控制台和 log 檔案會顯示:

最佳實踐與注意事項
1. 使用具名 Logger
# ❌ 不推薦:使用 root logger
import logging
logging.info("訊息")
# ✅ 推薦:使用具名 logger
logger = logging.getLogger(__name__)
logger.info("訊息")
2. 避免重複添加 Handler
# 清除舊的 handlers
for handler in logger.handlers[:]:
logger.removeHandler(handler)
# 再添加新的 handlers
logger.addHandler(new_handler)
3. 使用 Logger 層級控制輸出
# 開發環境:顯示所有訊息
logger.setLevel(logging.DEBUG)
# 生產環境:只顯示重要訊息
logger.setLevel(logging.WARNING)
4. 使用 f-string 或 lazy formatting
# ❌ 效能較差
logger.debug("User: " + username + ", Age: " + str(age))
# ✅ 推薦方式
logger.debug("User: %s, Age: %d", username, age)
# ✅ 也可以(Python 3.6+)
logger.debug(f"User: {username}, Age: {age}")
5. 異常處理時記錄完整追蹤資訊
try:
risky_operation()
except Exception as e:
# ✅ 記錄完整的 traceback
logger.exception("操作失敗")
# 或
logger.error("操作失敗", exc_info=True)
6. 不同環境使用不同配置
import os
if os.getenv('ENV') == 'production':
logging.basicConfig(
level=logging.WARNING,
filename='app.log'
)
else:
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s | %(levelname)s | %(message)s'
)
7. 定期輪替日誌檔案
from logging.handlers import RotatingFileHandler
# 檔案大小達到 10MB 時自動輪替,保留 5 個備份
handler = RotatingFileHandler(
'app.log',
maxBytes=10*1024*1024, # 10 MB
backupCount=5,
encoding='utf-8'
)
logger.addHandler(handler)
8. 按時間輪替日誌
from logging.handlers import TimedRotatingFileHandler
# 每天午夜輪替,保留 30 天
handler = TimedRotatingFileHandler(
'app.log',
when='midnight',
interval=1,
backupCount=30,
encoding='utf-8'
)
logger.addHandler(handler)
總結
Logging 是 Python 開發中不可或缺的工具,掌握它能讓你的程式更容易除錯、監控和維護。記住以下重點:
- 使用具名 Logger 而不是 root logger
- 合理設定層級 來控制訊息輸出
- 善用 Handler 實現多目標輸出
- 統一格式 提升日誌可讀性
- 記錄關鍵操作 和異常資訊
- 定期輪替檔案 避免日誌過大
透過本文的範例和最佳實踐,你應該能夠在自己的專案中建立完善的日誌系統了!
推薦hahow線上學習python: https://igrape.net/30afN
近期留言