目錄
- 什麼是 Logging?
- 為什麼需要 Logging?
- Logging 的五個層級
- 基礎使用方式
- 進階配置:Logger、Handler、Formatter
- 實戰案例:文件處理日誌系統
- 最佳實踐與注意事項
什麼是 Logging?
Logging 是 Python 內建的日誌記錄模組,用於追蹤程式執行過程中發生的事件。它比單純使用 print()
更強大,提供了分級記錄、格式化輸出、多目標輸出等功能。
為什麼需要 Logging?
相較於 print()
,Logging 有以下優勢:
- 分級管理:可以區分 DEBUG、INFO、WARNING、ERROR、CRITICAL 等不同嚴重程度
- 靈活輸出:可同時輸出到檔案、控制台、網路等多個目標
- 生產環境友善:可以在不修改程式碼的情況下調整日誌層級
- 格式化:統一的時間戳記、函數名稱、行號等資訊
- 效能:可以輕鬆關閉低層級日誌,提升效能
Logging 的五個層級
從低到高的嚴重程度:
![Python Logging 完全指南:從基礎到實戰應用; import logging ; logging.basicConfig(level=logging.INFO, handlers=[ logging.StreamHandler(), logging.FileHandler('app.log', mode='a', encoding='utf-8')] ) ; inspect.currentframe().f_code.co_name #動態取得funcName - 儲蓄保險王](https://savingking.com.tw/wp-content/uploads/2025/10/20251021160204_0_e2bb8d-1200x438.png)
基礎使用方式
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, handlers=[ logging.StreamHandler(), logging.FileHandler('app.log', mode='a', encoding='utf-8')] ) ; inspect.currentframe().f_code.co_name #動態取得funcName - 儲蓄保險王](https://savingking.com.tw/wp-content/uploads/2025/10/20251021155823_0_c16012.png)
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, handlers=[ logging.StreamHandler(), logging.FileHandler('app.log', mode='a', encoding='utf-8')] ) ; inspect.currentframe().f_code.co_name #動態取得funcName - 儲蓄保險王](https://savingking.com.tw/wp-content/uploads/2025/10/20251021160702_0_43a637.png)
logging.basicConfig() 在 Jupyter Notebook 的問題
"""
=== logging.basicConfig() 在 Jupyter Notebook 的問題 ===
問題根源:
logging.basicConfig() 的內部邏輯:
def basicConfig(**kwargs):
if len(root.handlers) == 0: # ← 只在這個條件成立時才配置
# 添加 handlers...
# 否則什麼都不做(靜默失敗)
Jupyter Notebook 的特性:
第一次執行 cell:
root.handlers = [] → basicConfig() 添加 handlers → ✅ 成功配置
第二次執行 cell:
root.handlers = [handler1, ...] → basicConfig() 檢測到已有 handlers
→ ❌ 直接返回,不做任何事
解決方案對比:
┌────────────────────────────────────────────────────────────────┐
│ 方案 1: force=True(Python 3.8+) │
├────────────────────────────────────────────────────────────────┤
│ logging.basicConfig( │
│ level=logging.DEBUG, │
│ format='%(asctime)s | %(message)s', │
│ force=True # ← 一行搞定 │
│ ) │
│ │
│ ✅ 優點:簡單、會自動 close() handlers │
│ ❌ 限制:只清除 root logger、需要 Python 3.8+ │
└────────────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────────┐
│ 方案 2: 手動清除(完整版) │
├────────────────────────────────────────────────────────────────┤
│ # 步驟 1: 關閉並移除舊 handlers │
│ for handler in logging.root.handlers[:]: │
│ handler.close() # ← 關閉資源(釋放檔案) │
│ logging.root.removeHandler(handler) # ← 移除參照 │
│ │
│ # 步驟 2: 重新配置 │
│ logging.basicConfig( │
│ level=logging.DEBUG, │
│ format='%(asctime)s | %(message)s' │
│ ) │
│ │
│ ✅ 優點:完整、相容所有 Python 版本、正確釋放資源 │
│ ❌ 缺點:需要兩個步驟 │
└────────────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────────┐
│ 方案 3: 簡化版(不推薦) │
├────────────────────────────────────────────────────────────────┤
│ logging.root.handlers.clear() # ⚠️ 只清空列表 │
│ logging.basicConfig(...) │
│ │
│ ❌ 問題:不會呼叫 handler.close(),資源不會釋放 │
│ ⚠️ 影響:FileHandler 的檔案無法刪除、可能資源洩漏 │
│ ✅ 適用:只有 StreamHandler 的情況 │
└────────────────────────────────────────────────────────────────┘
關鍵差異:
clear() vs close() + removeHandler()
handlers.clear():
- 只清空 handlers 列表
- ❌ 不會關閉檔案
- ❌ 不會釋放網路連線
- ❌ 可能造成資源洩漏
handler.close() + removeHandler():
- ✅ 關閉檔案(FileHandler)
- ✅ 關閉 socket(SocketHandler)
- ✅ 刷新緩衝區
- ✅ 釋放系統資源
- ✅ 然後才移除 handler
自定義 logger 的處理:
注意:force=True 只清除 root logger!
如果你有自定義 logger:
logger = logging.getLogger('MyApp')
logger.addHandler(...)
需要手動清除:
for handler in logger.handlers[:]:
handler.close()
logger.removeHandler(handler)
推薦做法(Jupyter Notebook):
# 方法 1:只用 root logger(最簡單)
logging.basicConfig(..., force=True)
logger = logging.getLogger('MyApp') # 自動使用 root 的 handlers
# 方法 2:需要自定義 logger
def reset_all_logging():
# 清除所有 logger
for name in list(logging.Logger.manager.loggerDict.keys()):
logger = logging.getLogger(name)
for handler in logger.handlers[:]:
handler.close()
logger.removeHandler(handler)
# 清除 root logger
for handler in logging.root.handlers[:]:
handler.close()
logging.root.removeHandler(handler)
reset_all_logging()
logging.basicConfig(..., force=True)
"""
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("發生錯誤!")
輸出:
![Python Logging 完全指南:從基礎到實戰應用; import logging ; logging.basicConfig(level=logging.INFO, handlers=[ logging.StreamHandler(), logging.FileHandler('app.log', mode='a', encoding='utf-8')] ) ; inspect.currentframe().f_code.co_name #動態取得funcName - 儲蓄保險王](https://savingking.com.tw/wp-content/uploads/2025/10/20251021162231_0_ed95d3.png)
格式化選項說明
常用的格式化參數:
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, handlers=[ logging.StreamHandler(), logging.FileHandler('app.log', mode='a', encoding='utf-8')] ) ; inspect.currentframe().f_code.co_name #動態取得funcName - 儲蓄保險王](https://savingking.com.tw/wp-content/uploads/2025/10/20251021162753_0_3ec814.png)
實戰案例:文件處理日誌系統
以下是一個完整的文件處理日誌系統,
展示如何在實際專案中使用 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, handlers=[ logging.StreamHandler(), logging.FileHandler('app.log', mode='a', encoding='utf-8')] ) ; inspect.currentframe().f_code.co_name #動態取得funcName - 儲蓄保險王](https://savingking.com.tw/wp-content/uploads/2025/10/20251021163330_0_c6dd69.png)
最佳實踐與注意事項
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
basicConfig()
可以同時輸出到 console 和檔案
一、最簡單的方式:basicConfig()
的 handlers
參數
✅ 推薦做法(Python 3.3+)
import logging
# 清除舊配置(Jupyter 環境)
logging.root.handlers.clear()
# 同時輸出到終端和檔案
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s | %(name)-20s | %(levelname)-8s | %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
handlers=[
logging.StreamHandler(), # 終端
logging.FileHandler('app.log') # 檔案
],
force=True
)
# 使用
logger = logging.getLogger('MyApp')
logger.info("這會同時顯示在終端和寫入檔案")
logger.warning("警告訊息")
優點:
- ✅ 只需要
basicConfig()
,不用手動管理 handler - ✅ 終端和檔案使用相同格式
- ✅ 程式碼簡潔
二、舊方法:分別設定 filename
和 stream
⚠️ 這種方法有限制
import logging
import sys
# 只能二選一!
logging.basicConfig(
level=logging.INFO,
format='%(levelname)s | %(message)s',
filename='app.log' # ❌ 只會輸出到檔案,不會顯示在終端
)
# 或
logging.basicConfig(
level=logging.INFO,
format='%(levelname)s | %(message)s',
stream=sys.stdout # ❌ 只會輸出到終端,不會寫入檔案
)
結論:basicConfig()
的 filename
和 stream
參數是互斥的,無法同時使用。
三、完整範例對比
方法 1:handlers
參數(推薦)
# %%
import logging
# 重置
logging.root.handlers.clear()
# 同時輸出到終端和檔案
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s | %(name)-15s | %(levelname)-8s | %(message)s',
handlers=[
logging.StreamHandler(), # 終端
logging.FileHandler(r'D:\Temp\app.log', mode='w', encoding="UTF-8"),
# 檔案(覆蓋模式)
],
force=True
)
# 建立 logger
logger_app = logging.getLogger('MyApp')
logger_db = logging.getLogger('Database')
# 測試
logger_app.info("應用程式啟動")
logger_db.debug("SQL 查詢: SELECT * FROM users")
logger_db.warning("查詢耗時過長")
輸出(終端和 app.log 內容相同):
![Python Logging 完全指南:從基礎到實戰應用; import logging ; logging.basicConfig(level=logging.INFO, handlers=[ logging.StreamHandler(), logging.FileHandler('app.log', mode='a', encoding='utf-8')] ) ; inspect.currentframe().f_code.co_name #動態取得funcName - 儲蓄保險王](https://savingking.com.tw/wp-content/uploads/2025/10/20251022103525_0_6686aa.png)
app.log
![Python Logging 完全指南:從基礎到實戰應用; import logging ; logging.basicConfig(level=logging.INFO, handlers=[ logging.StreamHandler(), logging.FileHandler('app.log', mode='a', encoding='utf-8')] ) ; inspect.currentframe().f_code.co_name #動態取得funcName - 儲蓄保險王](https://savingking.com.tw/wp-content/uploads/2025/10/20251022103646_0_331cba.png)
方法 2:手動添加 handler(相同格式)
如果不用 handlers
參數,等效的手動寫法:
import logging
# 重置
logging.root.handlers.clear()
# 手動配置 root logger
root_logger = logging.getLogger()
root_logger.setLevel(logging.DEBUG)
# 統一格式
formatter = logging.Formatter(
'%(asctime)s | %(name)-15s | %(levelname)-8s | %(message)s'
)
# Handler 1: 終端
console_handler = logging.StreamHandler()
console_handler.setFormatter(formatter)
# Handler 2: 檔案
file_handler = logging.FileHandler('app.log', mode='w')
file_handler.setFormatter(formatter)
# 添加到 root logger
root_logger.addHandler(console_handler)
root_logger.addHandler(file_handler)
# 使用
logger = logging.getLogger('MyApp')
logger.info("測試訊息")
對比:
- 方法 1(
handlers
參數):6 行 - 方法 2(手動):15 行
四、進階:終端和檔案使用不同格式/級別
如果你需要:
- 終端只顯示
INFO
以上,格式簡潔 - 檔案記錄
DEBUG
以上,格式詳細
這時才需要手動設定 handler:
import logging
# 重置
logging.root.handlers.clear()
# 建立 logger
logger = logging.getLogger('MyApp')
logger.setLevel(logging.DEBUG) # logger 本身接收所有級別
logger.propagate = False # 不傳遞給 root
# ========================================
# Handler 1: 終端(簡潔格式,只顯示 INFO+)
# ========================================
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO) # 只處理 INFO 以上
console_handler.setFormatter(
logging.Formatter('%(levelname)s | %(message)s')
)
# ========================================
# Handler 2: 檔案(詳細格式,記錄 DEBUG+)
# ========================================
file_handler = logging.FileHandler('debug.log', mode='w')
file_handler.setLevel(logging.DEBUG) # 記錄所有級別
file_handler.setFormatter(
logging.Formatter('%(asctime)s | %(name)s | %(levelname)s | %(message)s')
)
# 添加到 logger
logger.addHandler(console_handler)
logger.addHandler(file_handler)
# ========================================
# 測試
# ========================================
logger.debug("這是偵錯訊息") # 只在 debug.log
logger.info("這是一般訊息") # 終端 + debug.log
logger.warning("這是警告訊息") # 終端 + debug.log
終端輸出:
![Python Logging 完全指南:從基礎到實戰應用; import logging ; logging.basicConfig(level=logging.INFO, handlers=[ logging.StreamHandler(), logging.FileHandler('app.log', mode='a', encoding='utf-8')] ) ; inspect.currentframe().f_code.co_name #動態取得funcName - 儲蓄保險王](https://savingking.com.tw/wp-content/uploads/2025/10/20251022104124_0_e21998.png)
debug.log 內容:
![Python Logging 完全指南:從基礎到實戰應用; import logging ; logging.basicConfig(level=logging.INFO, handlers=[ logging.StreamHandler(), logging.FileHandler('app.log', mode='a', encoding='utf-8')] ) ; inspect.currentframe().f_code.co_name #動態取得funcName - 儲蓄保險王](https://savingking.com.tw/wp-content/uploads/2025/10/20251022104249_0_0e05b4.png)
五、決策樹
需要同時輸出到終端和檔案?
│
├─ 終端和檔案使用相同格式和級別?
│ └─ ✅ 用 basicConfig(handlers=[...]) ← 推薦
│
└─ 終端和檔案使用不同格式或級別?
└─ ⚠️ 手動添加 handler
總結表格
![Python Logging 完全指南:從基礎到實戰應用; import logging ; logging.basicConfig(level=logging.INFO, handlers=[ logging.StreamHandler(), logging.FileHandler('app.log', mode='a', encoding='utf-8')] ) ; inspect.currentframe().f_code.co_name #動態取得funcName - 儲蓄保險王](https://savingking.com.tw/wp-content/uploads/2025/10/20251022104818_0_8c2af8.png)
最佳實踐建議
✅ 推薦流程
- 先嘗試
basicConfig(handlers=[...])
- 如果格式/級別需求不同,再考慮手動 handler
- 在 Jupyter 中,記得使用
force=True
和handlers.clear()
✅ 完整模板(複製即用)
import logging
# Jupyter 重置
logging.root.handlers.clear()
# 同時輸出到終端和檔案
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s | %(name)-20s | %(levelname)-8s | %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
handlers=[
logging.StreamHandler(),
logging.FileHandler('app.log', mode='a', encoding='utf-8') # 'a' 追加模式
],
force=True
)
# 建立 logger
logger = logging.getLogger(__name__)
# 使用
logger.info("訊息會同時顯示在終端和寫入 app.log")
不需要手動設定 handler,用 basicConfig(handlers=[...])
就可以了! 🎉
推薦hahow線上學習python: https://igrape.net/30afN
近期留言