在Python中我们经常需要使用到多进程来提高我们程序性能,但是多进程的编程中经常有各种各样的问题来困扰我们,比如多进程和多线程的公用导致的子进程的卡死,进程间的通信等问题.还有一个问题我们也许不经常注意到,就是日志的记录.对于一些一次性的任务来说这个问题基本不存在,但是对于一些需要长期运行的Python任务,以及日志记录会比较多的任务来说,比如我们经常使用的各种Web框架,例如Flask和Tornado以及Django等.
1. 我们通常使用的Python的自带模块logging来记录日志,但是官方文档已经明确指出:这个模块不支持多进程,支持多线程.所以对于多进程的实现就需要我们自己来完成了.
2. 其实网络上已经有许多对于logging模块的多进程实现,基本都是使用了文件锁fcntl来实现的,我们也参考他们使用该模块,logging模块打印日志的都是由最终的hangdler实现的,所以我们只需要实现一个支持多进程打印日志的Handler即可.
3. 分析BaseRotatingHandler模块并结合官方文档,我们发现emit方法是最终处理日志输出的方法以及进行日志轮转的地方于是我们将锁加在这里,我们就将锁加在这个函数上面.
defemit(self, record):"""Emit a record.
Output the record to the file, catering for rollover as described
in doRollover()."""
try:ifself.shouldRollover(record):
self.doRollover()
logging.FileHandler.emit(self, record)exceptException:
self.handleError(record)
4. 文件锁的使用,fcntl的使用
这个锁只在linux上可以使用,但是一般来说我们的运行环境都是在liunx上其实问题不大.这个锁有好几种,我们这里只需要做到进程间的日志打印,所以使用排他锁.
importfcntl
file_path= "/home/ubuntu/aaa.json"f= open(file_path, 'w')
fcntl.flock(f.fileno(), fcntl.LOCK_EX)#加锁,其它进程对文件操作则不能成功
f.write("something")
fcntl.flock(f.fileno(), fcntl.LOCK_UN)#解锁
f.close()
5. 具体实现
importloggingimportosimportfcntlfrom logging.handlers importRotatingFileHandlerclassMyRotatingFileHandler(RotatingFileHandler):defemit(self, record):"""Emit a record.
Output the record to the file, catering for rollover as described
in doRollover()."""f=None