在前 12 天,我們已經建立了環境、依賴、目錄結構、測試、設定與秘密管理的基礎。專案「能跑、能測、能設定」。
但還缺一個關鍵拼圖:日誌(Logging)。
日誌不只是 debug 工具,它更是系統運行過程中的「黑盒紀錄器」。
如果沒有良好的日誌,你會遇到這些情況:
print()
。因此,我們需要從 「文字日誌」進化到「結構化日誌」,讓日誌不只是人能讀,還能被機器分析。
Python 內建的 logging
模組,是最常見的起手式:
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
logger.info("user logged in: %s", "alice")
logger.error("failed to fetch data: %s", "timeout")
輸出結果:
INFO:__main__:user logged in: alice
ERROR:__main__:failed to fetch data: timeout
👉 優點:
👉 缺點:
structlog
是 Python 生態中最成熟的結構化日誌套件,它的理念是:
日誌不應只是字串,而是 key-value 結構化資料。
安裝:
pip install structlog
使用:
import structlog
logger = structlog.get_logger()
logger.info("user_logged_in", user="alice", ip="127.0.0.1")
logger.error("db_error", query="SELECT * FROM users", error="timeout")
輸出結果(預設 JSON 格式化器後):
{"event": "user_logged_in", "user": "alice", "ip": "127.0.0.1", "level": "info"}
{"event": "db_error", "query": "SELECT * FROM users", "error": "timeout", "level": "error"}
👉 好處:
logging
整合,不用重寫所有程式碼。在容器化與雲端部署的場景(如 Kubernetes),日誌最佳實務就是 JSON 輸出:
設定範例(structlog + JSONRenderer):
import logging
import structlog
logging.basicConfig(level=logging.INFO)
structlog.configure(
processors=[
structlog.processors.add_log_level,
structlog.processors.TimeStamper(fmt="iso"),
structlog.processors.JSONRenderer()
]
)
logger = structlog.get_logger()
logger.info("payment_success", order_id="A123", amount=100)
輸出結果:
{
"event": "payment_success",
"order_id": "A123",
"amount": 100,
"level": "info",
"timestamp": "2025-09-21T16:00:00Z"
}
👉 優點:
order_id
或 level
快速搜尋、聚合。延續 Day 4 的 src/ + tests/
結構,可以把日誌設定集中到一個檔案:
my_project/
├─ src/my_project/
│ ├─ __init__.py
│ ├─ logging_config.py # logging/structlog 設定
│ ├─ core.py
│ └─ adapters/
│ └─ web.py
└─ tests/
└─ test_logging.py
logging_config.py
:
import logging
import structlog
def setup_logging():
logging.basicConfig(level=logging.INFO)
structlog.configure(
processors=[
structlog.processors.add_log_level,
structlog.processors.TimeStamper(fmt="iso"),
structlog.processors.JSONRenderer()
]
)
然後在 main.py
或 FastAPI/CLI entrypoint 啟動時呼叫 setup_logging()
,即可統一全專案日誌行為。
pytest 提供 caplog
fixture,可以驗證日誌輸出(延續 Day 11 的測試策略):
def test_logging_json(caplog):
import json
from my_project.logging_config import setup_logging
import structlog
setup_logging()
logger = structlog.get_logger()
logger.info("test_event", key="value")
log = json.loads(caplog.text.splitlines()[0])
assert log["event"] == "test_event"
assert log["key"] == "value"
logging
:小型專案或快速原型。structlog
:結構化、可擴充,適合進階專案。日誌是專案的「黑盒紀錄器」。
從傳統字串到結構化 JSON,我們能讓日誌不只給人看,更能被機器理解與分析。
這正是工程化 Python 的精神:讓每一份輸出都能成為可靠的資料流。 🚀
明天 Day 14,我們將探討 失敗即常態:例外分層、重試與降級(tenacity),看看如何把「錯誤」設計成系統的一部分,而不是臨時的補丁。