【问题标题】:Python TimedRotatingFileHandler overwrites logsPython TimedRotatingFileHandler 覆盖日志
【发布时间】:2018-02-02 08:31:01
【问题描述】:

我这样设置TimedRotatingFileHandler:

import logging as logging
from logging.handlers import TimedRotatingFileHandler
import os
import time

logger = logging.getLogger()
logger.setLevel(logging.DEBUG)

# new file every minute
rotation_logging_handler = TimedRotatingFileHandler('logs/log', 
                               when='m', 
                               interval=1, 
                               backupCount=5)
rotation_logging_handler.setLevel(logging.DEBUG)

format = u'%(asctime)s\t%(levelname)s\t%(filename)s:%(lineno)d\t%(message)s'
rotation_logging_handler.setFormatter(logging.Formatter(format))
rotation_logging_handler.suffix = '%Y-%m-%d'

logger.addHandler(rotation_logging_handler)

用法:

logger.logging.info('Service started at port %s', config.get_param('port'))

while True:
    time.sleep(21)
    logger.logging.info('Now time is {}'.format(time.time()))

我预计来自logs/log 的每分钟新消息都必须附加到当前日期的现有日志文件中。相反,来自logs/log 的每分钟消息都会覆盖当前日期的现有日志文件。

我应该怎么做才能达到这种行为?

PS:经过小小的研究,我发现TimedRotatingFileHandler 在doRollover 方法中删除了现有的日志文件并创建了新文件。所以第一个解决方案是创建从TimedRotatingFileHandler 派生的新处理程序 它会创建新文件(例如带有一些索引),用于删除现有的日志文件。

【问题讨论】:

    标签: python logging


    【解决方案1】:

    经过更多研究,我发现BaseRotatingHandler.rotation_filename method 中的BaseRotatingHandler.namer 属性用法:

    默认实现调用处理程序的“namer”属性,如果它是可调用的,则将默认名称传递给它。如果属性不可调用(默认为 None),则返回名称不变。

    因此,作为解决方案,我实现了自己的 namer 函数,该函数得到了 filename 并使用我的模板返回了新的 filename:

    • 20181231.log
    • 20181231.0.log
    • 20181231.1.log
    • 等

    完整示例:

    import logging as logging
    from logging.handlers import TimedRotatingFileHandler
    import os
    import time
    
    
    def get_filename(filename):
        # Get logs directory
        log_directory = os.path.split(filename)[0]
    
        # Get file extension (also it's a suffix's value (i.e. ".20181231")) without dot
        date = os.path.splitext(filename)[1][1:]
    
        # Create new file name
        filename = os.path.join(log_directory, date)
    
        # I don't want to add index if only one log file will exists for date
        if not os.path.exists('{}.log'.format(filename)):
            return '{}.log'.format(filename)
    
        # Create new file name with index
        index = 0
        f = '{}.{}.log'.format(filename, index)
        while os.path.exists(f):
            index += 1
            f = '{}.{}.log'.format(filename, index)
        return f
    
    
    format = u'%(asctime)s\t%(levelname)s\t%(filename)s:%(lineno)d\t%(message)s'
    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)
    
    # new file every minute
    rotation_logging_handler = TimedRotatingFileHandler('logs/log', 
                                   when='m', 
                                   interval=1, 
                                   backupCount=5)
    rotation_logging_handler.setLevel(logging.DEBUG)
    rotation_logging_handler.setFormatter(logging.Formatter(format))
    rotation_logging_handler.suffix = '%Y%m%d'
    rotation_logging_handler.namer = get_filename
    
    logger.addHandler(rotation_logging_handler)
    

    【讨论】:

      【解决方案2】:

      根据TimedRotatingFileHandler的文档:

      系统将通过附加扩展名来保存旧的日志文件 文件名。扩展是基于日期和时间的,使用 strftime 格式 %Y-%m-%d_%H-%M-%S 或其前导部分,具体取决于 翻转间隔。

      换句话说:通过修改后缀你打破了翻转。只需将其保留为默认值,python 将创建名为:

      • logs/log2018-02-02-01-30
      • logs/log2018-02-02-01-31
      • logs/log2018-02-02-01-32
      • logs/log2018-02-02-01-33
      • logs/log2018-02-02-01-34

      在此之后(如果backupCount=5)它将删除-30 并创建-35。

      如果您想使用类似的名称:

      • logs/log2018-02-02-01.0
      • logs/log2018-02-02-01.1
      • logs/log2018-02-02-01.2
      • logs/log2018-02-02-01.3
      • logs/log2018-02-02-01.4

      如果0 是最新的,.4 是最旧的,那么是的,该处理程序并非设计用于执行此操作。

      【讨论】:

      • 这是可能的解决方案之一,但我需要索引文件,所以它不适合我。正如您在我的回答中看到的那样,我找到了另一种可能的解决方案。
      • @feeeper 试图滥用一个类来做一些它原本不打算做的事情通常不是正确的前进方向。
      • 正如我在 Python 的文档中看到的那样,这是正确的做法docs.python.org/3/howto/…
      【解决方案3】:

      通过覆盖BaseRotatingHandler 类的rotation_filename 方法并将其设置为适当的可调用对象,很有可能在将文件旋转为您想要的任何内容时更改文件名。

      这是一个非常简单的例子,你可以根据自己的需要进行调整。

      import logging
      from logging.handlers import TimedRotatingFileHandler
      import datetime as dt
      
      def filer(self):
          now = dt.datetime.now()
          return 'file.txt'+now.strftime("%Y-%m-%d_%H:%M:%S")
      
      
      logger = logging.getLogger()
      rotating_file_handler = TimedRotatingFileHandler(filename="/Users/rbhanot/file.txt",
                                                       when='S',
                                                       interval=2,
                                                       backupCount=5)
      rotating_file_handler.rotation_filename = filer
      formatter = logging.Formatter(
          '%(asctime)s %(name)s:%(levelname)s - %(message)s')
      rotating_file_handler.setFormatter(formatter)
      logger.addHandler(rotating_file_handler)
      logger.setLevel(logging.DEBUG)
      
      logger.info("hello")
      

      这是输出

      ❯ ls file*
      file.txt  file.txt2020-10-06_13:12:13  file.txt2020-10-06_13:12:15  file.txt2020-10-06_13:13:45
      

      【讨论】:

      • 此答案无效...您的filer() 不正确,应将原始滚动文件名作为参数并对其进行修改,例如:def filer(file_name): return file_name+".log"
      • 这非常具体到你的实现,我的回答的要点是如何使用rotation_filename,而不是进入具体的实现。
      • 好的,我重新检查错误,我使用的是Windows 机器,因此无法在其中创建带有: 的文件名,所以请注意,要使此代码正常工作需要使确保从filer 函数返回的文件名适合操作系统。附注:在filer 函数中使用硬编码的file.txt 名称不是通用的,您应该使用:return self+now.strftime("%Y-%m-%d_%H:%M:%S")
      猜你喜欢
      • 1970-01-01
      • 2017-05-24
      • 1970-01-01
      • 2012-01-12
      • 2017-12-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-13
      相关资源
      最近更新 更多