很早以前有一位老同事和我說 Python 日誌模塊多進程不安全,沒辦法多個進程同時寫一個日誌文件。當時只是看了一下 官方文檔 確實明確的說是 Thread Safe,就也沒有深究這個問題。
這幾天正好看一個項目裏用了 ConcurrentLogHandler 包,忽然想仔細看一下 Python 日誌模塊到底在多進程下有啥問題。
TL;DR;
- 正常情況下,如果不做日誌 Rotate,不會有任何問題
- 如果多進程 Rotate 日誌文件,會出問題
- 這一切都和讀寫文件沒什麼關係
上面說的正常情況下,是指 Handle
初始化的時候模式要用默認的 mode='a'
1. 正常情況下,如果不做日誌 Rotate,不會有任何問題
Python 的 logging.FileHandler
日誌讀寫依賴的是什麼?如果看過 logging.FileHandler
日誌模塊的源碼就知道該模塊在初始化的時候會調用 open
打開一個文件輸出流,每次寫日誌就是 write
+ flush
的過程。
def emit(self, record):
try:
msg = self.format(record)
stream = self.stream
stream.write(msg)
stream.write(self.terminator)
self.flush()
except Exception:
self.handleError(record)
而我們知道,python 在寫文件的過程中,如果是通過 mode='a'
打開的文件,多進程寫文件不會出現任何問題(見文末參考),所以,mode='a'
的情況下,多進程同時寫日誌文件不會出現任何問題
2. 如果多進程 Rotate 日誌文件,會出問題
但是如果多個進程同時 Rotate 文件,就會出現問題。
一個典型的 case 是一個進程已經 Rotate 並寫入 Rotate 後的文件了;
另一個進程此時恰巧進行 Rotate,就會刪掉第一次創建的新文件,這期間第一個進程寫入的日誌就會丟失。
網上還有人說發現了特定情況下,同時寫新舊兩個日誌文件的情況,這個我也沒有遇到過,暫時也沒有腦細胞分析會不會出現這種情況
3. 如何解決
相關的解決辦法網上已經很多了,也不是本文討論的重點,例如:
- 重寫 FileRotateHandler,目前我們線上程序就是沿着這個思路做的
- 使用 ConcurrentLogHandler 包
備註:
A. ConcurrentLogHandler 的作用
我們可以看一下該模塊官方文檔是怎麼說的:
Python logging handler that allows multiple processes to safely write to the same log file concurrently. This module extends the standard RotatingFileHandler functionality, and can be use as a drop-in replacement for that logging class.
Python 官方的 logging handler 允許多進程對日誌文件的安全讀寫。本模塊擴展了 RotatingFileHandler
的功能,可以用來直接替換原來的 RotatingFileHandler
。