# 為什麼 `print()` 在 Docker 無法正確顯示?從緩衝區談起 最近在協助同事將開發環境容器化(使用 Docker)時,發現一個有趣的現象:在 container 中執行 FastAPI server 時,`print()` 的輸出無法即時顯示在終端機上,但使用 `logger` 卻沒有這個問題。這篇文章就來簡單記錄一下這背後的原因。 --- ### 先從緩衝區(Buffer)談起 緩衝區是一塊暫時儲存資料的記憶體空間,用來「暫存」資料,等到條件符合時再一次性地輸出或處理。這樣的設計可以減少 I/O 操作的頻率,提升效能。 以輸出到螢幕的 stdout buffering 為例,常見的緩衝模式有三種: - **Fully Buffered(全緩衝)** 資料會先存到 buffer 中,直到 buffer 滿了才會一次性輸出。常見於輸出到檔案或管道(pipe)時。 - **Line Buffered(行緩衝)** 每當遇到換行符號(`\n`)時,才會把 buffer 的內容輸出。常見於輸出到終端機。 - **Unbuffered(無緩衝)** 資料會立即輸出,不經過緩衝區。常見於錯誤輸出(stderr)或明確設定為無緩衝的情況。 --- ### 為什麼 `print()` 在 Docker 裡沒反應? 在使用 Docker Compose 執行 FastAPI server 時,我們發現 `print()` 的訊息無法即時顯示在終端機上。這是因為在 container 環境中,Python 的 stdout 預設會使用「全緩衝」或「行緩衝」,導致訊息被暫存在 buffer 中,直到條件符合才輸出。 相對地,`logger` 使用的 `StreamHandler` 預設會在每次 log 後自動 flush,因此訊息能即時顯示。 --- ### 解法:讓 `print()` 即時輸出 如果你仍想使用 `print()`,可以透過以下方式強制 Python 使用無緩衝模式: 1. **設定環境變數** ```bash PYTHONUNBUFFERED=1 ``` 2. 執行 Python 時加上 -u 參數 ``` $ python -u app.py ``` 3. 使用 flush=True(Python 3.3+) ``` print("Hello, world", flush=True) ``` --- ### 小結:為什麼還是推薦用 logger? 雖然可以讓 print() 即時輸出,但每次輸出都會觸發一次 system call,對效能還是會有一定影響。在 Production 環境中,建議使用 Python 的 logging 模組來處理輸出訊息,不僅效能好,也能透過設定 level, handler, formatter 來達到有彈性的配置。