← Back to list

Python 的 logging 用法全教學

剛開始學習 Python 時,我們最常用來除錯的幫手就是 print(),不管是要檢查某個變數的值、確定程式有沒有跑 if 判斷式、或是追蹤迴圈的進度,用 print()把值印出來看看,通常能解決大多數的問題。

Ac Studio · 2026-08-02 16:16 · 0 claps · 25.1 min read paywalled
#python #程式設計 #資工 #編程 #programming
Open on Medium ↗
Wiki topics: 💻 · Programming

Python 的 logging 用法全教學

2020.02.01 Bangkok, Thailand

2020.02.01 Bangkok, Thailand

剛開始學習 Python 時,我們最常用來除錯的幫手就是 print(),不管是要檢查某個變數的值、確定程式有沒有跑 if 判斷式、或是追蹤迴圈的進度,用 print()把值印出來看看,通常能解決大多數的問題。

然而,隨著專案規模逐漸龐大、程式碼行數從幾十行擴張到幾千行,單純依賴 print()會變得很麻煩,並且會遇到以下問題:

  • 終端機被無用的資訊淹沒: 所有的輸出都混在一起,很難分辨哪行是不重要的測試訊息,哪行是系統發出的嚴重錯誤警告。
  • 缺乏關鍵的上下文資訊: 當錯誤發生時,print() 預設不會告訴你這行字是幾點幾分印出的、來自哪個檔案,又或者是出自哪一個函式,所以除錯時又要花力氣再整理一次資訊。
  • 上線前的維護夢魘: 當程式準備部署到正式環境時,為了避免暴露敏感資訊或拖慢效能,你必須全域搜尋並手動刪除 (或註解掉) 一堆print();一旦又需要除錯,你還得再把它們一個個加回來。
  • 難以有效儲存與管理: print() 預設只能將內容輸出到螢幕上,如果想要同時將紀錄寫入檔案留存、或是根據錯誤嚴重程度發送到不同的監控系統,處理起來會非常吃力。

因此,本文要來介紹一個 Python 裡面可以解決這個問題的模組 - logging

分級機制

相比於陽春的 print()logging 提供了強大的分級機制 (從 DEBUGCRITICAL),不僅能自動補齊時間、檔案位置等上下文,還能透過簡單的設定,決定哪些訊息要顯示在螢幕上、哪些訊息要默默寫入日誌檔中。

logging 模組最特別的設計,就是將輸出的訊息依照「嚴重程度」分級,也就是說,針對每一條內容,你可以決定它的嚴重程度,並且在執行專案時決定要印出哪個層級以上的內容。

Python 內建了五個標準的日誌層級,嚴重程度由低到高分別為

  • DEBUG (級別 10): 最詳細的資訊,通常只有在開發階段或除錯 (Debug)時才會用到。例如:印出迴圈當下的變數值、API 請求的詳細參數。
  • INFO (級別 20): 用來確認程式正按照預期正常運作,記錄系統的日常狀態。例如:「伺服器已成功啟動」、「資料庫連線建立成功」、「使用者已登入」。
  • WARNING (級別 30): 警告訊息。表示發生了某些非預期的狀況,或未來可能會出問題,但程式目前仍在正常執行。例如:「磁碟空間即將不足」、「使用了準備廢棄的函式」(這是 Python logging 的預設層級)
  • ERROR (級別 40): 發生了錯誤,導致程式的某個特定功能無法正常執行。例如:「讀取檔案失敗」、「API 回傳 404 找不到網頁」。
  • CRITICAL (級別 50): 最嚴重的錯誤,通常代表程式本身已經無法繼續執行,即將崩潰或強制關閉。例如:「記憶體耗盡 (Out of Memory)」、「硬體連線全面中斷」。

執行 logging 程式

了解了層級之後,我們來看看最基礎的用法。你可以直接載入 logging 模組,並呼叫對應層級的方法:

import logging

logging.debug("這是一條 DEBUG 訊息:用來除錯的詳細資料")
logging.info("這是一條 INFO 訊息:程式正常運作中")
logging.warning("這是一條 WARNING 訊息:請注意,某個變數值怪怪的")
logging.error("這是一條 ERROR 訊息:糟糕,讀不到設定檔")
logging.critical("這是一條 CRITICAL 訊息:系統崩潰啦!")

如果把這段程式碼貼到 Python 裡執行,終端機只會印出以下三行:

WARNING:root:這是一條 WARNING 訊息:請注意,某個變數值怪怪的
ERROR:root:這是一條 ERROR 訊息:糟糕,讀不到設定檔
CRITICAL:root:這是一條 CRITICAL 訊息:系統崩潰啦!

為什麼 DEBUGINFO 不見了? 這就是 loggingprint() 聰明的地方。正如前面所提,Python logging 預設的日誌層級是 WARNING。這代表只有嚴重程度「大於或等於 WARNING」的訊息才會被輸出。這個機制完美解決了我們前面提到的痛點:在正式上線的環境中,你只需要把層級設高,那些繁瑣的 DEBUG 訊息就會自動被隱藏,再也不用手動去刪除它們!

調整層級與格式

如果你在開發階段,就是想要看到 DEBUGINFO 訊息,或是覺得預設的輸出格式太醜,少了我們最需要的「時間」和「檔案位置」,這時就可以使用 logging.basicConfig() 來做全域的基礎設定。

import logging

# 進行基礎設定
logging.basicConfig(
    level=logging.DEBUG,  # 將顯示層級調低到 DEBUG,這樣所有訊息都會顯示
    format='%(asctime)s - %(filename)s - %(levelname)s - %(message)s', # 自訂輸出格式
    datefmt='%Y-%m-%d %H:%M:%S' # 自訂時間格式
)

logging.debug("現在你看得到 DEBUG 訊息了!")
logging.error("這是一條帶有時間戳記的錯誤訊息")

裡面每一個被 %(...)s 包起來的東西,都是一個特殊的變數 (結尾的 s 代表把它轉換成字串 String),以下是它們各自代表的意思:

  • **%(asctime)s(發生時間,ASCII Time):** 記錄這行程式碼被執行的確切時間。它印出來的時間格式,會直接受到下方 datefmt='%Y-%m-%d %H:%M:%S' 的控制 (也就是 年-月-日 時:分:秒)。
  • **%(filename)s(檔案名稱,File Name):** 印出是哪一個 Python 檔案發出了這條日誌。以我們剛才的例子來說,它會印出 main.py
  • **%(levelname)s(日誌層級名稱,Level Name):** 印出這條紀錄的嚴重程度。例如 DEBUGINFOERROR
  • **%(message)s(你輸入的訊息,Message):** 這就是你在括號裡面真正想要印出來的那句話。例如 "現在你看得到 DEBUG 訊息

執行後的輸出結果會變成這樣:

2026-07-27 22:50:01 - main.py - DEBUG - 現在你看得到 DEBUG 訊息了!
2026-07-27 22:50:01 - main.py - ERROR - 這是一條帶有時間戳記的錯誤訊息

透過簡單的一行 basicConfig,我們不僅修改了觸發層級,還自動補上了精準的發生時間 (%(asctime)s)、發生檔案 (%(filename)s) 以及訊息層級 (%(levelname)s)。除錯時,這點上下文資訊可以幫我們省下大把的時間。

將日誌寫入檔案

到目前為止,我們的日誌都只印在終端機 (螢幕) 上。這在開發階段很方便,但如果你的程式是準備要長期運行的網路爬蟲、或是架設在伺服器上的後端 API,你總不可能 24 小時盯著螢幕看。如果半夜發生了 ERROR,隔天早上螢幕早就被其他資訊洗版了,要怎麼追查?

因此,將日誌儲存成實體的 .log 檔案,是實務上絕對必備的技巧。

好消息是,在 Python 裡要把日誌存成檔案非常簡單,我們只需要在剛剛學過的 basicConfig() 裡面,多加上幾個參數就可以了:

import logging

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    filename='my_app.log',  # 指定輸出的日誌檔案名稱
    filemode='a',           # 檔案寫入模式:'a' (附加) 或 'w' (覆寫)
    encoding='utf-8'        # 建議加上 utf-8,避免輸出中文時變成亂碼 (Python 3.9+)
)

logging.info("系統啟動,開始執行任務...")
logging.warning("API 回應速度稍微變慢,但不影響運作")
logging.error("找不到目標檔案 data.csv!")

當你執行這段程式碼時,你會發現終端機什麼都沒有印出來。這代表程式成功了。去檢查你的資料夾,你會發現多了一個名為 my_app.log 的檔案。打開它,裡面完整記錄了:

2026-07-28 11:32:57,123 - INFO - 系統啟動,開始執行任務...
2026-07-28 11:32:57,124 - WARNING - API 回應速度稍微變慢,但不影響運作
2026-07-28 11:32:57,125 - ERROR - 找不到目標檔案 data.csv!

filemode是可以調整的,比較常見的是 'a''w' 兩種模式。

  • **'a' (Append / 附加):** 這是預設的模式。每次執行程式時,新的日誌都會「接續」寫在檔案的最尾端。這通常是我們最想要的行為,因為你不會希望每次重啟程式就把昨天的紀錄都刪光。
  • **'w' (Write / 覆寫):** 如果你希望每次執行程式時,都會把舊的日誌清空,從頭開始記錄,就可以把模式改成 'w'。這在反覆測試同一小段程式碼時很方便。

學會了 basicConfig 之後,你可能會遇到一個新的困境:

如果我加了 filename,終端機就看不到日誌了;如果我不加 filename,又沒辦法存成檔案。難道不能兩個都要嗎?

除此之外,如果你負責的是一個大型專案,你可能會希望:

  1. 一般的 INFO 訊息印在螢幕上就好。
  2. 只要發生 ERROR 等級以上的錯誤,不僅要印在螢幕上,還要存進 error.log 檔案裡。
  3. 如果是第三方的套件 (例如 requests) 發出的日誌,把它過濾掉。

當你的需求變得如此靈活時,單靠一個簡單的 logging.basicConfig() 已經不夠用了。這時候,我們就要進入 logging 模組真正的強大之處——也就是它的「四大核心組件」:Logger (紀錄器)Handler (處理器) 與 Formatter (格式器)

Logger、Handler 與 Formatter

前面學到的 logging.basicConfig() 雖然方便、快速,但所有的設定 (包含層級、格式、輸出位置) 都是全域 (Global) 綁死在一起的。

如同我們在設計大型物件導向 (OOP) 系統時,會傾向將不同的職責拆分到不同的類別 (Class) 以提高彈性;logging 模組的底層,其實也是基於這種優雅的解耦架構。要實現「同時輸出螢幕與檔案」,甚至「螢幕只顯示 INFO,檔案卻專門記錄 ERROR」,我們需要認識 logging 的三大核心元件:

Logger (紀錄器): 它是你的專屬秘書,也是你在程式碼中唯一需要互動的物件 (例如呼叫 logger.info())。它的工作是接收你傳來的訊息,並根據嚴重程度判斷是否要往下傳遞。

Handler (處理器): 它是物流中心。當 Logger 決定放行一條訊息後,會交給 Handler 決定「要把訊息送到哪裡」。一個 Logger 可以同時綁定多個 Handler。常見的 Handler 包括:

  • StreamHandler:負責將訊息發送到終端機 (螢幕)。
  • FileHandler:負責將訊息寫入檔案。

Formatter (格式器): 它是包裝設計師。負責決定訊息最終呈現的排版格式 (包含時間、檔名等)。你可以為不同的 Handler 配置不同的 Formatter。

現在我們就可以自己組裝一個多功能的 logger

import logging

# 步驟 1:建立並命名 Logger (你的專屬秘書)
# 慣例上會使用 __name__ 當作名稱,這樣能輕易追蹤這條日誌是哪個 Python 檔案發出的
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)  # 設定 Logger 的總接收門檻為最低的 DEBUG

# 步驟 2:建立 Handlers (物流中心)
# 2.1 螢幕輸出處理器:我們希望螢幕畫面乾淨一點,只顯示 INFO 等級以上的訊息
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO) 

# 2.2 檔案輸出處理器:我們希望檔案只記錄 ERROR 等級以上的嚴重錯誤
file_handler = logging.FileHandler('error.log', encoding='utf-8')
file_handler.setLevel(logging.ERROR)

# 步驟 3:建立 Formatters (包裝設計師)
# 螢幕上的格式可以簡單一點,檔案裡的格式我們希望詳細記錄時間
console_format = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
file_format = logging.Formatter('%(asctime)s - [%(levelname)s] 在 %(filename)s 中發生錯誤:%(message)s')

# 將格式器綁定到對應的處理器上
console_handler.setFormatter(console_format)
file_handler.setFormatter(file_format)

# 步驟 4:把處理器全部綁定到 Logger 身上
logger.addHandler(console_handler)
logger.addHandler(file_handler)

# ================= 測試時間 =================

logger.debug("這是一條 DEBUG:Logger 收到了,但兩個 Handler 的門檻都比較高,所以這條會被丟掉。")
logger.info("這是一條 INFO:會出現在螢幕上,但不會寫入 error.log。")
logger.warning("這是一條 WARNING:螢幕上看得到,同樣不會寫入檔案。")
logger.error("這是一條 ERROR:螢幕上看得到,而且終於被寫入 error.log 裡了!")

當你執行這段程式碼時,終端機 (螢幕) 會印出:

__main__ - INFO - 這是一條 INFO:會出現在螢幕上,但不會寫入 error.log。
__main__ - WARNING - 這是一條 WARNING:螢幕上看得到,同樣不會寫入檔案。
__main__ - ERROR - 這是一條 ERROR:螢幕上看得到,而且終於被寫入 error.log 裡了!

同時,資料夾中會生成一個 error.log 檔案,裡面精準地只記錄了錯誤資訊,並且帶有完整的時間與檔案出處:

2026-07-28 13:50:42,881 - [ERROR] 在 main.py 中發生錯誤:這是一條 ERROR:螢幕上看得到,而且終於被寫入 error.log 裡了!

透過這種物件導向的組裝方式,你完全掌握了日誌的控制權。日後不管系統怎麼擴展,想要把日誌發送到 Email、寫入資料庫、或是推送到雲端監控服務,都只需要「再多掛載一個對應的 Handler」就能無痛達成,原本的程式碼一行都不用改。

在多個 Python 檔案中部屬 logger

當你的專案從單一腳本,成長為包含 main.py(主程式)、database.py(資料庫操作)、api.py(網路請求) 等多個模組的大型專案時,你馬上會面臨一個現實的問題:

難道我剛剛寫的那一大串建立 Logger、Handler、Formatter 的程式碼,要在每一個 Python 檔案裡都複製貼上一次嗎?

這麼做很沒效率,也很容易出錯,因為未來如果你想修改日誌的時間格式,還要打開幾十個檔案一個一個改。

Python 的 logging 模組在設計時,就已經預判了這個需求。logging 內部採用了一種類似「樹狀結構」的層級設計,並且具有自動「向上傳遞」的特性。

簡單來說,你只需要在專案的入口點 (通常是 main.py)統一設定好一次 Handler 和 Formatter。在其他所有的子模組 (如 database.py) 中,只要用一行程式碼呼叫專屬的 Logger,它就會自動「繼承」主程式的設定。

讓我們來看看實務上到底怎麼寫:

1. 主程式 (main.py):負責統一設定

在主程式中,我們進行全局的配置。這裡為了示範簡潔,我們使用 basicConfig 來設定根 (Root) Logger,當然你也可以用上一段學到的進階組裝法。

# main.py
import logging
import database  # 載入你的另一個 Python 模組

# 在程式的進入點,統一設定好日誌的格式與輸出位置
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - [%(name)s] - %(levelname)s - %(message)s'
)

# 建立 main.py 專屬的 logger
logger = logging.getLogger(__name__)

def main():
    logger.info("主程式啟動,準備連接資料庫...")
    database.connect_db()  # 呼叫另一個檔案的函式
    logger.info("主程式執行完畢。")

if __name__ == "__main__":
    main()

2. 子模組 (database.py):只負責發出日誌

在其他的模組裡,你完全不需要再設定任何格式或檔案名稱:

# database.py
import logging

# 關鍵:直接用 __name__ 建立專屬這個檔案的 logger
logger = logging.getLogger(__name__)

def connect_db():
    logger.info("正在嘗試連接到 PostgreSQL...")
    # 假設這裡發生了錯誤
    logger.error("連接超時!請檢查資料庫伺服器狀態。")

看看執行的結果:

當你執行 python main.py 時,終端機會完美印出以下內容:

2026-07-28 14:15:30,110 - [__main__] - INFO - 主程式啟動,準備連接資料庫...
2026-07-28 14:15:30,111 - [database] - INFO - 正在嘗試連接到 PostgreSQL...
2026-07-28 14:15:30,112 - [database] - ERROR - 連接超時!請檢查資料庫伺服器狀態。
2026-07-28 14:15:30,113 - [__main__] - INFO - 主程式執行完畢。

仔細看上面的輸出結果,有沒有發現中括號 [...] 裡面的名稱不一樣?

  • main.py 裡,__name__ 的值是 __main__
  • database.py 裡,__name__ 的值則是模組名稱 database

當你在 database.py 呼叫 logging.getLogger(__name__) 時,它會去尋找名為 database 的 Logger。因為你沒有特別為它設定 Handler,它就會乖乖地把訊息向上層傳遞,最後交由 main.py 中設定好的全域規則來輸出。

這種「每個檔案都擁有自己名字的 Logger,但共享同一套輸出規則」的作法,能讓你在未來的除錯過程中,一眼就看出這行錯誤到底是從哪一個模組、哪一個檔案噴出來的,乾淨俐落。

logger.exception做錯誤追蹤

在寫 Python 程式時,為了避免程式因為突發狀況而整個崩潰死當,我們通常會使用 try...except 區塊來捕捉並處理例外狀況 (Exceptions)。

很多初學者在剛換掉 print()改用 logging 時,在 except 區塊裡這樣寫:

import logging
logger = logging.getLogger(__name__)

def divide(a, b):
    return a / b

try:
    result = divide(10, 0)
except Exception as e:
    # 新手常見寫法:只記錄了錯誤訊息字串
    logger.error(f"糟糕,發生錯誤了:{e}")

如果你執行這段程式碼,終端機確實會印出: ERROR - 糟糕,發生錯誤了:division by zero

在這麼簡單的程式裡,你當然一眼就知道是因為 10 / 0 導致的錯誤。但想像一下,如果你是在幾千行程式碼的大型專案裡,呼叫了十幾個層層嵌套的函式,最後看到這句 division by zero,你絕對會崩潰——因為你根本不知道這個錯誤是在哪一個檔案、哪一行程式碼發生的!

單純使用 logger.error(),會把 Python 內建那串最有價值的「錯誤追蹤訊息 (Traceback)」給吞掉。為了解決這個問題,logging 模組提供了一個專門在 except 區塊內使用的殺手級方法:logger.exception()

我們把剛剛的程式碼稍微改一行:

try:
    result = divide(10, 0)
except Exception as e:
    # 內行寫法:使用 logger.exception
    logger.exception("計算過程發生嚴重的錯誤!")

再一次執行程式碼,看看這次終端機會印出什麼:

ERROR - 計算過程發生嚴重的錯誤!
Traceback (most recent call last):
  File "main.py", line 7, in <module>
    result = divide(10, 0)
  File "main.py", line 4, in divide
    return a / b
ZeroDivisionError: division by zero

logger.exception() 不僅會以 ERROR 層級記錄你自訂的錯誤提示訊息,它還會在背景默默幫你加上 exc_info=True 的參數,自動將最完整的 Traceback 原封不動地印出來 (或是寫入你的日誌檔案裡)。注意到 exception()的等級歸類在 error(),它是專門給 except 抓錯用的工具。

這樣一來,你的程式既不會因為未處理的錯誤而中斷服務,你也能在日誌檔案中保留最詳細的案發現場線索,讓後續的除錯工作變得輕而易舉。

日誌切割

當你學會了將日誌寫入檔案後,如果你的程式是一個需要 24 小時不間斷運行的服務 (例如:網站後端、Discord 機器人或是長時間的數據收集爬蟲),你很快就會面臨一個新的問題:日誌檔無限膨脹。

想像一下,你的程式每天產生 50 MB 的日誌,一個月後這個 app.log 就會變成 1.5 GB。不但會吃光你伺服器的硬碟空間,當你想打開這個檔案來查錯誤時,普通的文字編輯器還會直接當機卡死。

為了解決這個問題,業界標準的作法是進行「日誌切割 (Log Rotation)」。在 logging.handlers 模組中,提供了兩種非常實用的進階處理器:

1. 按檔案大小切割:RotatingFileHandler

如果你希望嚴格控管日誌檔案所佔用的總硬碟空間,這是最好的選擇。你可以設定當檔案達到某個容量上限時,自動將舊檔案備份,並建立一個新的空檔案繼續寫入。

import logging
from logging.handlers import RotatingFileHandler

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

# 設定:當檔案達到 5MB 時自動切割,最多保留 3 份舊備份
handler = RotatingFileHandler(
    'my_app.log', 
    maxBytes=5 * 1024 * 1024,  # 5 MB (單位是 Bytes)
    backupCount=3,             # 保留 my_app.log.1, my_app.log.2, my_app.log.3
    encoding='utf-8'
)

formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)

它的運作機制是這樣的: 程式會一直把日誌寫入 my_app.log。當這個檔案超過 5MB 時,Python 會自動把它重新命名為 my_app.log.1,然後建立一個全新的 my_app.log 繼續寫。當再次滿 5MB 時,.1 會變成 .2,新的滿檔變成 .1。以此類推,超過 3 份的最舊備份就會被系統無情地刪除,永遠不會撐爆你的硬碟。

2. 按時間切割:TimedRotatingFileHandler

如果你的專案需要按天來查核日誌 (例如:老闆請你查「昨天晚上 10 點」的連線錯誤),那麼按時間切割會更直覺。

import logging
from logging.handlers import TimedRotatingFileHandler

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

# 設定:每天午夜 (midnight) 進行一次切割,保留最近 7 天的紀錄
handler = TimedRotatingFileHandler(
    'daily_app.log', 
    when='midnight',   # 切割時機:午夜
    interval=1,        # 每 1 個單位 (天) 切割一次
    backupCount=7,     # 留存 7 天
    encoding='utf-8'
)

formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)

它的運作機制是這樣的: 每天午夜一過,Python 就會把昨天的檔案加上日期後綴,例如變成 daily_app.log.2026-07-27,然後繼續把今天的日誌寫入全新的 daily_app.log 中。這對於每日資料備份與歷史追蹤來說,簡直是完美的設計。

用 dictConfig 解耦

當你的專案成長到一定規模,或者準備進入團隊協作的階段,你會發現另一個管理上的麻煩:在程式碼裡面寫一大堆建立 Logger、Handler 和 Formatter 的邏輯,實在是太佔版面了!

更致命的是,寫死在程式碼裡的設定缺乏彈性。想像一下,你的網站平常在正式環境中只記錄 INFO,某天半夜突然發生莫名的 Bug,你需要臨時把日誌層級降到 DEBUG 來收集更多線索。如果設定寫死在程式碼裡,你竟然需要修改程式、甚至重新部署整個服務,這在正式環境中是風險極高的操作。

為了達到「設定與程式碼分離」,業界最推崇的做法是使用 logging.config.dictConfig

dictConfig 允許我們將所有複雜的日誌架構(包含哪些 Formatter、哪些 Handler、以及各個 Logger 的層級路由) 全部定義在一個字典 (Dictionary)中。

實務上,開發者通常會把這份字典寫成外掛的 JSONYAML 檔案。這裡我們以直觀的 Python 字典為例,看看這能讓主程式碼變得多乾淨:

import logging
import logging.config
import yaml  # 實務上常搭配 yaml 模組讀取外部設定檔

# 將所有複雜的設定獨立成一個字典 (實務上會從 logging_config.yaml 讀取)
LOGGING_CONFIG = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'standard': {
            'format': '%(asctime)s - [%(levelname)s] - %(name)s - %(message)s'
        },
        'simple': {
            'format': '%(levelname)s - %(message)s'
        },
    },
    'handlers': {
        'console': {
            'level': 'INFO',
            'class': 'logging.StreamHandler',
            'formatter': 'simple',
        },
        'file': {
            'level': 'ERROR',
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': 'app_error.log',
            'maxBytes': 10485760,  # 10 MB
            'backupCount': 5,
            'formatter': 'standard',
        },
    },
    'loggers': {
        '': {  # 這是 Root Logger (根紀錄器)
            'handlers': ['console', 'file'],
            'level': 'DEBUG',
            'propagate': True
        }
    }
}

# 魔法發生在這裡:一行程式碼,瞬間套用所有複雜設定!
logging.config.dictConfig(LOGGING_CONFIG)

# 接下來的程式碼完全不用管設定,直接拿來用就好
logger = logging.getLogger(__name__)

logger.info("系統啟動!現在的主程式碼變得非常乾淨。")
logger.error("這條錯誤會按照設定檔的規則,安靜地寫入 app_error.log 裡,同時印在螢幕上。")

這樣有幾個好處:

  1. 程式整潔: 主程式 (或啟動檔) 裡再也看不到落落長的 Handler 與 Formatter 設定,開發者可以專注在主要內容上。
  2. 環境切換: 你可以準備兩份檔案:logging_dev.yaml(開發用,輸出 DEBUG) 和 logging_prod.yaml(正式用,只輸出 WARNING,且自動切割檔案)。程式啟動時,只要根據環境變數讀取對應的檔案即可。

學會了 dictConfig,現在你就具備基本的logging 知識了。

結語

這篇文章的目的是希望初學者在用了一陣子的 print()大法後,也學會用更完善的 logging 系統,短期內雖然會比直接寫 print() 多花一點點時間進行基礎設定,當你的專案在半夜發生未知的 Bug,或是幾個月後需要交接、進行系統維護時,這些結構化、帶有完整上下文且分類明確的日誌,會讓你輕鬆很多。

[embed]歡迎來到 Ac Studio!第一次來請先讀這篇 大家好!為了幫助各位讀者快速找到自己需要的內容,我們製作了這張知識地圖。如圖所示,目前我們的技術文章主要分為 8 個大類。希望這張地圖能幫助你快速上手,找到需要的資源。medium.com


메타데이터
post_id
d5508aab9671
slug
python-的-logging-用法全教學-d5508aab9671
url
https://medium.com/@acamvproducingstudio/python-%E7%9A%84-logging-%E7%94%A8%E6%B3%95%E5%85%A8%E6%95%99%E5%AD%B8-d5508aab9671
canonical_url
https://medium.com/@acamvproducingstudio/python-%E7%9A%84-logging-%E7%94%A8%E6%B3%95%E5%85%A8%E6%95%99%E5%AD%B8-d5508aab9671
author_url
https://medium.com/@acamvproducingstudio
status
ok
fetched_at
2026-08-07 06:05:25