Python 多进程日志记录
来源:互联网 发布:博时基金待遇 知乎 编辑:程序博客网 时间:2024/06/07 15:22
刚开始用 Python 做 web 开发的时候我就想一个问题,如果 Python 应用需要自己记录一些比 accesslog 更详细的日志(使用 Python 的 logging module),又有多个进程,怎么办最好呢?多个进程往同一个日志文件写入会不会出问题?
最近有个在 Apache 里用 mod_wsgi 运行的程序,设置了4个 process. 最初没有设置日志的 rotation,看起来一切正常。有一天设定了每天 midnight rotate(换成 TimedRotatingFileHandler), 第二天就出问题了,前一天的日志完全丢失,当天日志分散在前一天和当天的两个文件里,并且两个文件都在增长。比如今天是2011-08-14,现在去观察就会发现昨天的 customlog-20110813 和今天的 customlog 两个文件都在被写入。
看了一下 TimedRotatingFileHandler 的 doRollover 方法:
t = self.rolloverAt - self.interval if self.utc: timeTuple = time.gmtime(t) else: timeTuple = time.localtime(t) dfn = self.baseFilename + "." + time.strftime(self.suffix, timeTuple) if os.path.exists(dfn): os.remove(dfn) os.rename(self.baseFilename, dfn) ...
这就了然了。每个进程在过了 rotate 时间点之后写第一条日志的时候,都会执行这个 doRollover,先看有没有 customlog-20110813 存在,存在的话删掉,把 customlog 改名为 customlog-20110813(注意这一步,文件名只是文件的一个属性,如果有进程已经打开该文件正在写入,并不会受影响,除非文件被删除),然后往新的 customlog 里写入。等四个进程都执行完这个方法的时候,就是一团糟了,不仅昨天的日志完全被删除,今天的日志也会有一小部分被删掉了。
多进程 (Multiprocessing) vs. 多线程 (multithreading)
假如只有一个进程,就不用操心这些了。我这个 qingbo.net 一直都是设置1个进程。于是我在 Stack Overflow 上问,如果 multithreading 就够了,为啥还要 multiprocessing 呢?回答者 Graham 好像是 mod_wsgi 的作者,从他的回答看,对于 Python 来说,由于 GIL 的限制,多进程才能利用多 CPU 或 core 的架构。
好吧,想想有什么办法可以解决这个问题。
Write separate files
取得当前进程 ID (os.getpid()
),然后把这个 pid 作为日志文件名的一部分,这样各个进程的日志操作就完全不会互相影响了。
缺点是当服务器重启的时候,新的进程会有新的 pid,于是旧的文件不会被改成带有日期标识的名字,看起来不是那么干净。不过这个问题通过写一个简单的 shell 脚本即可修复,可以放在 crontab 里每天凌晨检查前一天的日志。
结合 WatchedFileHandler 与 TimedRotatingFileHandler?
以下是 WatchedFileHandler 的 emit 方法:
def emit(self, record): """ Emit a record. First check if the underlying file has changed, and if it has, close the old stream and reopen the file to get the current stream. """ if not os.path.exists(self.baseFilename): stat = None changed = 1 else: stat = os.stat(self.baseFilename) changed = (stat[ST_DEV] != self.dev) or (stat[ST_INO] != self.ino) if changed and self.stream is not None: self.stream.flush() self.stream.close() self.stream = self._open() if stat is None: stat = os.stat(self.baseFilename) self.dev, self.ino = stat[ST_DEV], stat[ST_INO] logging.FileHandler.emit(self, record)
每写一条日志之前它先判断日志文件是不是被改名了,如果是的话就重新创建一个。如果 TimedRotatingFileHandler 在 doRollover 之前做这么一个检查,不就解决问题了吗?不过实际上这又牵扯到进程间同步的问题,还挺复杂的。
WatchedFileHandler 结合 cron job
一个取巧的办法是使用 WatchedFileHandler,并在 crontab 里每天零点添加一个任务,把日志文件改名。于是所有的进程在之后写第一条日志的时候会发现这一点,开始往新的文件里写入。
我没有尝试这个办法所以不知到是不是有问题。但是它的前提是没一个写入操作是一个 atom operation,否则两条日志有可能混杂在一起,就坏了。我不太了解,不过这里有个讨论,看起来每条日志不超过 4K 的话是安全的。该文还展示了另一种办法——
将日志发给一个独立的进程
Python 的 logging module 还提供了一个 SocketHandler,用来把日志通过网络发送出去。文档里有一个非常基本的服务器端的实现。有人还基于 Twisted 写了一个更好的 logging server.
但是这样我们除了需要维护 web server,又要维护 logging server. 万一 logging server 挂了怎么办?我有点担心。
把日志发给 web server
其实也是发给一个独立进程了。mod_wsgi 提供了一种途径,可以把 debug 信息写到 Apache 的 errorlog 里。不过把应用日志跟服务器 errorlog 混在一起,这显然不是什么好办法。
比起多进程的解决方案,我更倾向于使用单线程省去这些烦恼。一方面是我的服务器比较弱,只有两个 core :) 正如 Graham 所说,除了 python code,还有许多处理比如接受、分发请求,发送响应回客户端,处理静态文件请求等都是在 Apache 的 C code里执行的,所以不会让一个 core 忙不过来另一个却闲着。我倒没有实际经验看看单个 Python 应用进程在4核或8核机器上面对大并发量的情况。
单进程的另一个好处,可以不用 memcached 之类的东西,自己直接在进程的内存里方便地作缓存。如果有多个进程这么搞,那就太浪费内存了。
- Python多进程记录日志
- Python 多进程日志记录
- python进程写文件与多进程记录日志
- python 日志记录
- python IDE && 日志记录
- python logging 日志记录
- python log 日志记录
- Python如何记录日志
- python 日志记录
- python日志记录
- Python中的日志记录
- python记录日志
- Python 多进程使用中的一些记录
- 20170621:python日志文件记录
- python的日志logging模块性能以及多进程
- python多进程检查端口并写日志
- python抓包截取http记录日志
- Python模块学习 ---- logging 日志记录
- 关于相对布局的一些属性
- 代理和块的区别
- Spark集群基于Zookeeper的HA搭建部署
- 【Android】混淆,fastjson
- Tomcat中配置https访问
- Python 多进程日志记录
- WIN7下 svn迁移到github
- sicily 1009. Mersenne Composite N
- linux ntpdate同步错误,差一个小时的问题
- 使用TabActivity实现底部菜单栏
- MySQL学习笔记2-System administration(set password)
- 位运算的运用
- System.out.println与System.err.println的区别
- priority_queue 优先队列使用方法