Python TimedRotatingFileHandler 多进程环境下的问题和解决方法

原文:https://my.oschina.net/lionets/blog/796438

Python 自带了一个 handler 可以实现每天自动切割日志文件的功能(其实支持各种按时间切割的方法,不过按日期切割是最常用的一种)。

切割 这件事的触发和执行逻辑可以从 BaseRotatingHandler(logging.FileHandler) 里看到:

def emit(self, record):"""Emit a record.Output the record to the file, catering for rollover as describedin doRollover()."""try:if self.shouldRollover(record):self.doRollover()logging.FileHandler.emit(self, record)except (KeyboardInterrupt, SystemExit):raiseexcept:self.handleError(record)

而 TimedRotatingFileHandler 只不过是依自己的逻辑实现了 self.shouldRollover 和 self.doRollover 方法。

共所周知的,这个 handler 在多进程环境下会有问题,比如丢失日志。这对于 web server 来说简直是个灾难。而这个问题的原因来自于它切割文件的方式,即 doRollover 方法。简化掉多余的代码后它看起来是这样的:

def doRollover(self):if self.stream:self.stream.close()self.stream = None# get the time that this sequence started at and make it a TimeTupledfn = self.baseFilename + "." + time.strftime(self.suffix, timeTuple)if os.path.exists(dfn):os.remove(dfn)if os.path.exists(self.baseFilename):os.rename(self.baseFilename, dfn)self.rolloverAt = newRolloverAt

假设配置的文件名是 error.log,日志正准备从 11.11 切换到 11.12。那么 self.baseFilename 就是 error.logdfn 就是 error.log.2016-11-11。即执行流程为:

  1. 判断准备切入的文件是否存在,如果是,删掉之
  2. 然后把 error.log 重命名为 error.log.2016-11-11
  3. doRollover 结束,FileHandler.emit 将消息写入 error.log (新建)

单进程环境中这个流程没问题,但是多进程下因为每个进程都会调用一次 doRollover,就可能会发生像一个进程已经 rollover 完成,但是下一个进程把之前的 error.log 又给删掉了之类的问题。

解决方法


最简单直接的一种方法其实是:把多进程的 log handler 配置改为一个 TimedRotatingFileHandler + N 个 FileHandler,这样就绕过了 多进程 这个环境,其余的进程只负责向 error.log 文件写。

这需要实现进程间的不同配置,可以通过一个锁来实现,第一个成功 acquire 到这个锁的进程进行 rollover。

然而这存在一个切割不精确的问题,即在切割进程成功进行 rollover 之前,其他进程会把新日志写进旧文件。因此更好的办法是去修改 TimedRotatingFileHandler。改这个类的方法多种多样,我的做法是抛弃文件重命名这个操作,将 rollover 的实现变为进程无冲的。方法为:写文件时始终向带日期后缀的文件写,然后做一个 error.log 的软链接,指向最新的一条日志。

这样就抛弃了重命名的过程,行为和 FileHandler 更加类似。软连接的删除和新建也不存在进程间冲突。

code(可以直接用,但没有处理 utc):

class MultiProcessSafeDailyRotatingFileHandler(BaseRotatingHandler):"""Similar with `logging.TimedRotatingFileHandler`, while this one is- Multi process safe- Rotate at midnight only- Utc not supported"""def __init__(self, filename, encoding=None, delay=False, utc=False, **kwargs):self.utc = utcself.suffix = "%Y-%m-%d"self.baseFilename = filenameself.currentFileName = self._compute_fn()BaseRotatingHandler.__init__(self, filename, 'a', encoding, delay)def shouldRollover(self, record):if self.currentFileName != self._compute_fn():return Truereturn Falsedef doRollover(self):if self.stream:self.stream.close()self.stream = Noneself.currentFileName = self._compute_fn()def _compute_fn(self):return self.baseFilename + "." + time.strftime(self.suffix, time.localtime())def _open(self):if self.encoding is None:stream = open(self.currentFileName, self.mode)else:stream = codecs.open(self.currentFileName, self.mode, self.encoding)# simulate file name structure of `logging.TimedRotatingFileHandler`if os.path.exists(self.baseFilename):try:os.remove(self.baseFilename)except OSError:passtry:os.symlink(self.currentFileName, self.baseFilename)except OSError:passreturn stream

Python TimedRotatingFileHandler 多进程环境下的问题和解决方法相关推荐

  1. php多进程共享数据库,PHP多进程环境下通过共享内存与信号量实现资源共享

    PHP多进程环境下通过共享内存与信号量实现资源共享 目前工作环境,由于一些原因,不能使用swoole,和其他多进程的管理组件.但是项目中有大量的功能必须通过多进程来实现.面对这也不能,那也不能的困境, ...

  2. python连接opencv库_python环境下安装opencv库的方法

    注意:安装opencv之前需要先安装numpy,matplotlib等 一.安装方法 方法一.在线安装 1.先安装opencv-python pip install opencv-python --u ...

  3. Windows+Python 3.6环境下安装PyQt4

    Windows+Python 3.6环境下PyQt4安装不上 文章目录: 一.PyQt4安装不上 二.正确安装PyQt4库包 最近在写的程序需要用到UI界面显示,然后又闻PyQt4可以干这个事,然后走 ...

  4. python的opencv库_python环境下安装opencv库的方法

    注意:安装opencv之前需要先安装numpy,matplotlib等 一.安装方法 方法一.在线安装 1.先安装opencv-python pip install opencv-python --u ...

  5. python3.8与pyinstaller_pyinstaller 3.5 在python 3.8 环境下出现不兼容的问题

    在python 3.8环境下使用pyinstaller 3.5版本打包制作*.exe文件总是如下报错, # pyinstaller -F abc.py ... 21100 INFO: checking ...

  6. Python在指定环境下安装第三方库的报错解决办法

    Python在指定环境下安装第三方库的报错解决办法 在python安装第三方库时,如果直接打开cmd命令提示符,并输入下列安装命令,则会默认安装在base环境下 但base环境下的包新建的虚拟环境是无 ...

  7. 解决python多版本环境下pip报错Fatal error in launcher: Unable to create process using问题

    在电脑上安装了多个python版本之后,执行pip2/pip3 list时会报错,因为python的主程序名称已被更改,导致找不到. 所以,只需要用python2 python3重新更新下pip程序即 ...

  8. Python安装及环境配置一文式解决

    目录 一.前言介绍 二.Anaconda的下载及安装 2.1.Anaconda的下载及安装 2.2.测试Anaconda是否安装成功 三.Cuda以及Cudnn的下载安装 3.1.1.Cuda以及Cu ...

  9. ros构建机器人运动学模型_ROS环境下的机器人仿真模型构建方法研究

    现代电子技术 Modern Electronics Technique 2018 年 4 月 1 日 第 41 卷第 7 期 Apr. 2018 Vol. 41 No. 7 DOI : 10.1665 ...

最新文章

  1. Ubuntu16.04 Table无法自动补全
  2. Snackbar源码分析
  3. 苹果iPhone被曝跟踪用户位置信息(图)
  4. 主机访问虚拟机中linux上的web服务
  5. SVN导致目录图标出现“?”号解决方案
  6. CentOS6实现路由器功能
  7. 计算机网络(二十三)-网络层-概述与数据交换方式
  8. 轻量级PHP接口框架PhalApi开源接口框架 v2.17.1源码
  9. 统计数字字符和空格 (15 分)
  10. Linux设备树OF API 中OF的含义
  11. 为什么黑客都用python-python为什么会作为黑客的首选语言?这几本书给你答案(已集齐)...
  12. memcache连接是否有用户名和密码的设置
  13. Spring Cloud Sleuth Zipkin - (1)
  14. 联想Y9000P安装Ubuntu18.04+PX4+mavros+QGC
  15. Vue 引入 zepto
  16. sql字符型注入-sqli第1关
  17. arm linux 俄罗斯方块,基于ARM的俄罗斯方块游戏的开发教材.doc
  18. HDU 1425 sort
  19. Java实现买火车票【抢票】成功率100%
  20. zemax 学习笔记

热门文章

  1. 服务器怎么修复插件,如何修复服务器状态代码:302由SQL发现我注入了Firefox插件...
  2. 工业机器人几个自由度_取件冲压上下料机械手和六自由度工业机器人805A
  3. RAM测试方法 C语言实现,有哪些常用单片机系统RAM测试方法?基于种子和逐位倒转的RAM故障测试法有什么优点?...
  4. python基本输入输出代码示例
  5. VXLAN和GRE的区别
  6. 二叉搜索树的最近祖先
  7. axure 导入元件库显示不出白框_AXURE免费元件库分享-web
  8. thinkphp mysql json数据类型_ThinkPHP:JSON字段类型的使用(ORM)
  9. java arraylist 删除回车符_2种Java删除ArrayList中的重复元素的方法
  10. spring_了解Spring Web应用程序体系结构:经典方法