python的logging库中TimedRotatingFileHandler类问题

2023-11-20 22:38

本文主要是介绍python的logging库中TimedRotatingFileHandler类问题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

原文网址:http://www.5dcode.com/?p=545

第一次用python,第一次用logging,第一次用TimedRotatingFileHandler,居然发现了其中的BUG,记录下吧。

      用TimedRotatingFileHandler的目的是让其自动在日志文件名后面加上日期时间,可以按秒、分、时、天、周或者其倍数来设置,BUG出现的场景是:手动设置时间,并把时间往未来时间调(比如把2012-03-15调成2014-03-15),这时就出问题了,这时产生每条日志后会产生一个日志文件,这并不是我们想要的效果,如果把当前时间再往历史时间调(比如把2012-03-15调成2010-03-15),这时也会产生问题:所有产生的日志都会记录到一个没有日期后缀的文件,并不会按日期分类。如果时间是正确的并按正常的流程走并不会产生问题,所以想看看logging是怎么实现的,看了其源码:C:\Python25\Lib\logging\handlers.py,果然不出所料,它的设计是有问题的,根本不考虑手动调时间或者时间可能不对需要同步的情况:

  1. def shouldRollover(self, record):
  2.         """
  3.         Determine if rollover should occur
  4.  
  5.         record is not used, as we are just comparing times, but it is needed so
  6.         the method siguratures are the same
  7.         """
  8.         t = int(time.time())
  9.         if t >= self.rolloverAt:
  10.             return 1
  11.         #print "No need to rollover: %d, %d" % (t, self.rolloverAt)
  12.         return 0
  13.  
  14.     def doRollover(self):
  15.         """
  16.         do a rollover; in this case, a date/time stamp is appended to the filename
  17.         when the rollover happens.  However, you want the file to be named for the
  18.         start of the interval, not the current time.  If there is a backup count,
  19.         then we have to get a list of matching filenames, sort them and remove
  20.         the one with the oldest suffix.
  21.         """
  22.         self.stream.close()
  23.         # get the time that this sequence started at and make it a TimeTuple
  24.         t = self.rolloverAt - self.interval
  25.         timeTuple = time.localtime(t)
  26.         dfn = self.baseFilename + "." + time.strftime(self.suffix, timeTuple)
  27.         if os.path.exists(dfn):
  28.             os.remove(dfn)
  29.         os.rename(self.baseFilename, dfn)
  30.         if self.backupCount > 0:
  31.             # find the oldest log file and delete it
  32.             s = glob.glob(self.baseFilename + ".20*")
  33.             if len(s) > self.backupCount:
  34.                 s.sort()
  35.                 os.remove(s[0])
  36.         #print "%s -> %s" % (self.baseFilename, dfn)
  37.         if self.encoding:
  38.             self.stream = codecs.open(self.baseFilename, 'w', self.encoding)
  39.         else:
  40.             self.stream = open(self.baseFilename, 'w')
  41.         self.rolloverAt = self.rolloverAt + self.interval

第9行判断中只判断时间大的情况,并没有判断时间小的情况,第41行中self.rolloverAt永远是上一次的值加上self.interval,如果时间往大的调的话,第9行判断就会永远是True,所以就会产生问题,如果时间往小的调,第9行判断就会永远是False,也会产生问题。上面的python版本是2.5.4,再看最新的版本:3.1,这个版本修复了def doRollover(self),但并没有修复def shouldRollover(self, record),所以综合这两个版本的考虑,还是自己来实现吧,修复之后的代码如下(测试成功通过):

 

  1. def shouldRollover(self, record):
  2.         """
  3.         Determine if rollover should occur
  4.  
  5.         record is not used, as we are just comparing times, but it is needed so
  6.         the method siguratures are the same
  7.         """
  8.         t = int(time.time())
  9.         #print "self.rolloverAt: %d. currentTime: %d." % (self.rolloverAt, t)
  10.         #start: recompare slef.rolloverAt, Modify by 5dcode. 2012-03-14 
  11.         if t >= self.rolloverAt or t < (self.rolloverAt - self.interval):
  12.             return 1
  13.         #end: recompare slef.rolloverAt, Modify by 5dcode. 2012-03-14 
  14.         #print "No need to rollover: %d, %d" % (t, self.rolloverAt)
  15.         return 0
  16.  
  17.     def doRollover(self):
  18.         """
  19.         do a rollover; in this case, a date/time stamp is appended to the filename
  20.         when the rollover happens.  However, you want the file to be named for the
  21.         start of the interval, not the current time.  If there is a backup count,
  22.         then we have to get a list of matching filenames, sort them and remove
  23.         the one with the oldest suffix.
  24.         """
  25.         self.stream.close()
  26.         # get the time that this sequence started at and make it a TimeTuple
  27.         t = self.rolloverAt - self.interval
  28.         timeTuple = time.localtime(t)
  29.         dfn = self.baseFilename + "." + time.strftime(self.suffix, timeTuple)
  30.         if os.path.exists(dfn):
  31.             os.remove(dfn)
  32.         os.rename(self.baseFilename, dfn)
  33.         if self.backupCount > 0:
  34.             # find the oldest log file and delete it
  35.             s = glob.glob(self.baseFilename + ".20*")
  36.             if len(s) > self.backupCount:
  37.                 s.sort()
  38.                 os.remove(s[0])
  39.         #print "%s -> %s" % (self.baseFilename, dfn)
  40.         if self.encoding:
  41.             self.stream = codecs.open(self.baseFilename, 'w', self.encoding)
  42.         else:
  43.             self.stream = open(self.baseFilename, 'w')
  44.         
  45.         #start: recompute self.rolloverAt, Modify by 5dcode. 2012-03-14 
  46.         currentTime = int(time.time())
  47.         newRolloverAt = currentTime + self.interval
  48.         if self.when == 'MIDNIGHT' or self.when.startswith('W'):
  49.             # This could be done with less code, but I wanted it to be clear
  50.             t = time.localtime(currentTime)
  51.             currentHour = t[3]
  52.             currentMinute = t[4]
  53.             currentSecond = t[5]
  54.             # r is the number of seconds left between now and midnight
  55.             r = _MIDNIGHT - ((currentHour * 60 + currentMinute) * 60 +
  56.                     currentSecond)
  57.             newRolloverAt = currentTime + r
  58.         #newRolloverAt = self.computeRollover(currentTime)
  59.         while newRolloverAt <= currentTime:
  60.             newRolloverAt = newRolloverAt + self.interval
  61.         #If DST changes and midnight or weekly rollover, adjust for this.
  62.         if self.when == 'MIDNIGHT' or self.when.startswith('W'):
  63.             dstNow = time.localtime(currentTime)[-1]
  64.             dstAtRollover = time.localtime(newRolloverAt)[-1]
  65.             if dstNow != dstAtRollover:
  66.                 if not dstNow:  # DST kicks in before next rollover, so we need to deduct an hour
  67.                     newRolloverAt = newRolloverAt - 3600
  68.                 else:           # DST bows out before next rollover, so we need to add an hour
  69.                     newRolloverAt = newRolloverAt + 3600
  70.         self.rolloverAt = newRolloverAt
  71.         #print "self.rolloverAt: %d." % self.rolloverAt
  72.         #end: recompute self.rolloverAt, Modify by 5dcode. 2012-03-14

 

 

 

 

这篇关于python的logging库中TimedRotatingFileHandler类问题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/397941

相关文章

基于Python编写一个git自动上传的脚本(打包成exe)

《基于Python编写一个git自动上传的脚本(打包成exe)》这篇文章主要为大家详细介绍了如何基于Python编写一个git自动上传的脚本并打包成exe,文中的示例代码讲解详细,感兴趣的小伙伴可以跟... 目录前言效果如下源码实现利用pyinstaller打包成exe利用ResourceHacker修改e

Python在二进制文件中进行数据搜索的实战指南

《Python在二进制文件中进行数据搜索的实战指南》在二进制文件中搜索特定数据是编程中常见的任务,尤其在日志分析、程序调试和二进制数据处理中尤为重要,下面我们就来看看如何使用Python实现这一功能吧... 目录简介1. 二进制文件搜索概述2. python二进制模式文件读取(rb)2.1 二进制模式与文本

Python中Tkinter GUI编程详细教程

《Python中TkinterGUI编程详细教程》Tkinter作为Python编程语言中构建GUI的一个重要组件,其教程对于任何希望将Python应用到实际编程中的开发者来说都是宝贵的资源,这篇文... 目录前言1. Tkinter 简介2. 第一个 Tkinter 程序3. 窗口和基础组件3.1 创建窗

Django调用外部Python程序的完整项目实战

《Django调用外部Python程序的完整项目实战》Django是一个强大的PythonWeb框架,它的设计理念简洁优雅,:本文主要介绍Django调用外部Python程序的完整项目实战,文中通... 目录一、为什么 Django 需要调用外部 python 程序二、三种常见的调用方式方式 1:直接 im

Python字符串处理方法超全攻略

《Python字符串处理方法超全攻略》字符串可以看作多个字符的按照先后顺序组合,相当于就是序列结构,意味着可以对它进行遍历、切片,:本文主要介绍Python字符串处理方法的相关资料,文中通过代码介... 目录一、基础知识:字符串的“不可变”特性与创建方式二、常用操作:80%场景的“万能工具箱”三、格式化方法

JAVA Calendar设置上个月时,日期不存在或错误提示问题及解决

《JAVACalendar设置上个月时,日期不存在或错误提示问题及解决》在使用Java的Calendar类设置上个月的日期时,如果遇到不存在的日期(如4月31日),默认会自动调整到下个月的相应日期(... 目录Java Calendar设置上个月时,日期不存在或错误提示java进行日期计算时如果出现不存在的

Mybatis对MySQL if 函数的不支持问题解读

《Mybatis对MySQLif函数的不支持问题解读》接手项目后,为了实现多租户功能,引入了Mybatis-plus,发现之前运行正常的SQL语句报错,原因是Mybatis不支持MySQL的if函... 目录MyBATis对mysql if 函数的不支持问题描述经过查询网上搜索资料找到原因解决方案总结Myb

浅析python如何去掉字符串中最后一个字符

《浅析python如何去掉字符串中最后一个字符》在Python中,字符串是不可变对象,因此无法直接修改原字符串,但可以通过生成新字符串的方式去掉最后一个字符,本文整理了三种高效方法,希望对大家有所帮助... 目录方法1:切片操作(最推荐)方法2:长度计算索引方法3:拼接剩余字符(不推荐,仅作演示)关键注意事

Nginx错误拦截转发 error_page的问题解决

《Nginx错误拦截转发error_page的问题解决》Nginx通过配置错误页面和请求处理机制,可以在请求失败时展示自定义错误页面,提升用户体验,下面就来介绍一下Nginx错误拦截转发error_... 目录1. 准备自定义错误页面2. 配置 Nginx 错误页面基础配置示例:3. 关键配置说明4. 生效

python版本切换工具pyenv的安装及用法

《python版本切换工具pyenv的安装及用法》Pyenv是管理Python版本的最佳工具之一,特别适合开发者和需要切换多个Python版本的用户,:本文主要介绍python版本切换工具pyen... 目录Pyenv 是什么?安装 Pyenv(MACOS)使用 Homebrew:配置 shell(zsh