Python的logging库为啥设计成不支持多进程?
而且 django 为何默认使用 logging 记录日志,难道 django 都是部署单进程的吗?
Python的logging库为啥设计成不支持多进程?
5 回复
这其实是个常见的误解。Python的logging库本身是支持多进程的,只是默认的FileHandler在多进程写入同一个日志文件时会有问题。
核心原因是:多个进程同时写入同一个文件时,操作系统层面的文件句柄和写入位置管理会导致日志内容错乱、覆盖或丢失。这不是logging库的设计缺陷,而是并发文件写入的通用挑战。
要解决这个问题,有几种标准做法:
- 使用
ConcurrentLogHandler或logging.handlers.QueueHandler(Python 3.2+) 这是官方推荐的方式。主进程设置日志处理器,子进程通过队列发送日志消息:
import logging
import logging.handlers
from multiprocessing import Process, Queue
import time
def worker_process(queue):
handler = logging.handlers.QueueHandler(queue)
logger = logging.getLogger()
logger.addHandler(handler)
logger.setLevel(logging.INFO)
# 这些日志消息会被发送到队列
logger.info('Worker process started')
logger.error('An error occurred')
def listener_process(queue):
while True:
try:
record = queue.get()
if record is None: # 终止信号
break
logger = logging.getLogger(record.name)
logger.handle(record)
except Exception:
import sys, traceback
print('Problem in listener:', file=sys.stderr)
traceback.print_exc(file=sys.stderr)
if __name__ == '__main__':
queue = Queue()
# 设置主进程的日志处理器(实际写入文件)
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(processName)s - %(message)s',
filename='app.log'
)
# 启动监听进程
listener = Process(target=listener_process, args=(queue,))
listener.start()
# 启动工作进程
workers = []
for i in range(3):
worker = Process(target=worker_process, args=(queue,))
workers.append(worker)
worker.start()
# 等待工作进程结束
for w in workers:
w.join()
# 发送终止信号给监听进程
queue.put(None)
listener.join()
- 使用第三方库如
concurrent-log-handler它提供了带锁的ConcurrentRotatingFileHandler:
from concurrent_log_handler import ConcurrentRotatingFileHandler
handler = ConcurrentRotatingFileHandler(
'app.log',
maxBytes=1024*1024,
backupCount=5
)
logging.getLogger().addHandler(handler)
-
每个进程写不同的日志文件 最简单的方案,但后期日志整合麻烦。
-
使用系统日志服务(如syslog) 适合Linux/Unix环境。
所以准确来说,logging库是支持多进程的,只是需要选择合适的处理器来避免文件写入冲突。对于生产环境,推荐使用QueueHandler方案,这是最稳定可靠的方式。
总结:用QueueHandler解决多进程日志问题。
可以用 logging 接口来自定义 log 服务啊,还可以用队列来异步化。
可以配置不同得日志文件

