Python Logging 完全指南:從基礎到實戰應用; import logging ; logging.basicConfig(level=logging.INFO) ; inspect.currentframe().f_code.co_name #動態取得funcName

加入好友
加入社群
Python Logging 完全指南:從基礎到實戰應用; import logging ; logging.basicConfig(level=logging.INFO) ; inspect.currentframe().f_code.co_name #動態取得funcName - 儲蓄保險王

目錄

  1. 什麼是 Logging?
  2. 為什麼需要 Logging?
  3. Logging 的五個層級
  4. 基礎使用方式
  5. 進階配置:Logger、Handler、Formatter
  6. 實戰案例:文件處理日誌系統
  7. 最佳實踐與注意事項

什麼是 Logging?

Logging 是 Python 內建的日誌記錄模組,用於追蹤程式執行過程中發生的事件。它比單純使用 print() 更強大,提供了分級記錄、格式化輸出、多目標輸出等功能。


為什麼需要 Logging?

相較於 print(),Logging 有以下優勢:

  • 分級管理:可以區分 DEBUG、INFO、WARNING、ERROR、CRITICAL 等不同嚴重程度
  • 靈活輸出:可同時輸出到檔案、控制台、網路等多個目標
  • 生產環境友善:可以在不修改程式碼的情況下調整日誌層級
  • 格式化:統一的時間戳記、函數名稱、行號等資訊
  • 效能:可以輕鬆關閉低層級日誌,提升效能

Logging 的五個層級

從低到高的嚴重程度:

Python Logging 完全指南:從基礎到實戰應用; import logging ; logging.basicConfig(level=logging.INFO) ; inspect.currentframe().f_code.co_name #動態取得funcName - 儲蓄保險王

基礎使用方式

1. 最簡單的 Logging

import logging

# 基本配置
logging.basicConfig(level=logging.INFO)

# 記錄不同層級的訊息
logging.debug("這是除錯訊息")      # 不會顯示層級太低
logging.info("這是一般訊息")       # 會顯示
logging.warning("這是警告訊息")    # 會顯示
logging.error("這是錯誤訊息")      # 會顯示
logging.critical("這是嚴重錯誤")   # 會顯示

輸出:

Python Logging 完全指南:從基礎到實戰應用; import logging ; logging.basicConfig(level=logging.INFO) ; inspect.currentframe().f_code.co_name #動態取得funcName - 儲蓄保險王

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

輸出:

Python Logging 完全指南:從基礎到實戰應用; import logging ; logging.basicConfig(level=logging.INFO) ; inspect.currentframe().f_code.co_name #動態取得funcName - 儲蓄保險王

在 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 系統由三個核心組件構成:

  1. Logger:日誌記錄器,負責發出日誌訊息
  2. Handler:處理器,決定日誌輸出到哪裡(檔案、控制台等)
  3. Formatter:格式器,定義日誌的輸出格式

架構圖解

LoggerHandlerFormatter輸出目標
         (可有多個)

完整範例:同時輸出到檔案和控制台

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("發生錯誤!")

輸出:

Python Logging 完全指南:從基礎到實戰應用; import logging ; logging.basicConfig(level=logging.INFO) ; inspect.currentframe().f_code.co_name #動態取得funcName - 儲蓄保險王

格式化選項說明

常用的格式化參數:

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

輸出:

Python Logging 完全指南:從基礎到實戰應用; import logging ; logging.basicConfig(level=logging.INFO) ; inspect.currentframe().f_code.co_name #動態取得funcName - 儲蓄保險王

實戰案例:文件處理日誌系統

以下是一個完整的文件處理日誌系統,
展示如何在實際專案中使用 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 檔案會顯示:

Python Logging 完全指南:從基礎到實戰應用; import logging ; logging.basicConfig(level=logging.INFO) ; inspect.currentframe().f_code.co_name #動態取得funcName - 儲蓄保險王

最佳實踐與注意事項

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 開發中不可或缺的工具,掌握它能讓你的程式更容易除錯、監控和維護。記住以下重點:

  1. 使用具名 Logger 而不是 root logger
  2. 合理設定層級 來控制訊息輸出
  3. 善用 Handler 實現多目標輸出
  4. 統一格式 提升日誌可讀性
  5. 記錄關鍵操作 和異常資訊
  6. 定期輪替檔案 避免日誌過大

透過本文的範例和最佳實踐,你應該能夠在自己的專案中建立完善的日誌系統了!

推薦hahow線上學習python: https://igrape.net/30afN

加入好友
加入社群
Python Logging 完全指南:從基礎到實戰應用; import logging ; logging.basicConfig(level=logging.INFO) ; inspect.currentframe().f_code.co_name #動態取得funcName - 儲蓄保險王

儲蓄保險王

儲蓄險是板主最喜愛的儲蓄工具,最喜愛的投資理財工具則是ETF,最喜愛的省錢工具則是信用卡

You may also like...

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *