Python 多进程日志记录

转载 2015年11月17日 15:50:56

刚开始用 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的日志logging模块性能以及多进程

写在前面:     日志是记录操作的一种好方式。但是日志,基本都是基于文件的,也就是要写到磁盘上的。这时候,磁盘将会成为一个性能瓶颈。对于普通的服务器硬盘(机械磁盘,非固态硬盘),python日志的...
  • chenggong2dm
  • chenggong2dm
  • 2016年08月02日 15:42
  • 3935

python进程写文件与多进程记录日志

假设有个进程,一直在写一个名字叫做1的文件,然后我们在它运行的过程中将文件改名了,会发生什么事情呢? 写个程序试试: 然后运行它,此时可以看到: 因为进程得到了文件的句柄,所以就算这个文件改名...
  • rongyongfeikai2
  • rongyongfeikai2
  • 2016年04月18日 21:26
  • 3406

Python 中 logging 日志模块在多进程环境下的使用

使用 Python 来写后台任务时,时常需要使用输出日志来记录程序运行的状态,并在发生错误时将错误的详细信息保存下来,以别调试和分析。Python 的 logging 模块就是这种情况下的好帮手。 ...
  • u011731378
  • u011731378
  • 2017年09月05日 16:38
  • 371

Python多进程编程入门

Python多进程编程入门  多进程(multiprocessing)模块是在 Python 2.6 版本加入的。它最初由 Jesse Noller 和 Richard Oudkerk 在 P...
  • Jinlong_Xu
  • Jinlong_Xu
  • 2017年03月28日 21:16
  • 394

Python多进程记录日志

用gevent(或封装了gevent的gunicore)启动python进程,会出现多个独立进程同时写一个日志文件, 可以观察到有日志部分丢失:一个进程日志没写完,另一个进程把日志覆盖在同一行的后面...
  • yongche_shi
  • yongche_shi
  • 2015年10月28日 18:23
  • 1489

python使用多进程或者多线程

使用python做计算的时候,为了加快速率,可以启用多进程或者多线程。那几时使用多进程或者多线程呢 如果是io型,使用多线程,如果是cpu型,使用多进程。 理论上说,使用多进程是效率最大的,而且i...
  • cyuanxin
  • cyuanxin
  • 2016年09月24日 13:56
  • 890

python multiprocessing logging

TODO
  • bachelorchen
  • bachelorchen
  • 2015年07月13日 20:44
  • 640

python 多进程共享变量

现在要读取多个数据文件从中抽取数据将结果合并(相当于word count) 方法一是常规的分多线程分别处理数据输出,最后定义一个字典遍历输出文件将结果集合并 方法二是先定义结果集字典,然后...
  • xys228280987
  • xys228280987
  • 2015年11月19日 11:31
  • 4384

python多进程探测端口写日日志(带cmd颜色显示)

  • 2017年04月05日 16:37
  • 4KB
  • 下载

Python学习多进程并发写入同一文件

最近学习了Python的多进程,想到我的高德API爬虫那个爬取读写速度我就心累,实在是慢,看到多进程可以充分利用CPU核数我就开始完善我的代码,不过过程是艰辛的,在此之中出现了很多问题,其中最大的问题...
  • Kompany4
  • Kompany4
  • 2017年09月26日 22:12
  • 1126
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Python 多进程日志记录
举报原因:
原因补充:

(最多只允许输入30个字)