很早以前有一位老同事和我说 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
。