【问题标题】:Is it possible to write many different log files using a single logger?是否可以使用单个记录器编写许多不同的日志文件?
【发布时间】:2013-03-27 05:16:57
【问题描述】:

我的应用程序在相当长的一段时间内管理多个对象(称为请求)的状态。每个请求都有一个唯一标识符,并经历不同的生命周期。随着时间的推移,系统中会出现新的请求。

我想为每个请求编写一个单独的日志文件。日志将跟踪该请求状态的每一个有趣的变化。因此,如果我想了解有关 Request X 历史的所有信息,只需查看 X.log。

显然,我可以使用纯文件手动推出解决方案。但我想用 Python 的日志框架来做到这一点。一种方法是为每个唯一请求创建一个新的记录器实例,将其配置为指向正确的文件,然后注销。但这感觉像是错误的解决方案。它创建了许多记录器,这些记录器不是垃圾收集的,而且也是无限的,因为新的请求将继续进入系统。

我希望有某种方法来配置单个记录器,也许使用自定义处理程序,以便我可以根据传入请求的 ID 将输出重定向到不同的文件。我查看了文档,但我看到的所有内容似乎都在传入记录级别工作,而不是操纵传出端点。

这可能吗?

【问题讨论】:

  • 您的 Request 对象是如何离开系统的?似乎您可以为每个请求创建一个 Logger 对象,并进行适当配置,然后当您的请求停用时,它可以告诉 Manager(logging.Manager 子类?)删除 Logger 实例。
  • @Matt Anderson - 这可能是可能的。很难知道请求何时完成,但也许我可以想出一些办法。我不确定如何删除记录器实例。它们是logging 模块的模块范围,并且似乎没有任何清理API。 del 足够了吗?

标签: python logging


【解决方案1】:

查看logging.handlersRotatingFileHandler 的代码最终给了我足够的线索来解决这个问题。关键实现是,当记录消息时,可以传递可选的extra 关键字,这是要存储在Record 中的属性字典。这可以从Handler 访问。在Handler中,我们可以根据用户提供的值触发输出流的变化。

import logging

class MultiFileHandler(logging.FileHandler):

    def __init__(self, filename, mode, encoding=None, delay=0):
        logging.FileHandler.__init__(self, filename, mode, encoding, delay)

    def emit(self, record):
        if self.should_change_file(record):
            self.change_file(record.file_id)
        logging.FileHandler.emit(self, record)

    def should_change_file(self, record):
        if not hasattr(record, 'file_id') or record.file_id == self.baseFilename:
             return False
        return True

    def change_file(self, file_id):
        self.stream.close()

        self.baseFilename = file_id
        self.stream = self._open()

if __name__ == '__main__':

    logger = logging.getLogger('request_logger')
    logger.setLevel(logging.DEBUG)    
    handler = MultiFileHandler(filename='out.log', mode='a')
    handler.setLevel(logging.DEBUG)    
    logger.addHandler(handler)

    # Log some messages to the original file
    logger.debug('debug message')
    logger.info('info message')

    # Log some messages to a different file
    logger.debug('debug message',       extra={'file_id':'changed.log'})
    logger.info('info message',         extra={'file_id':'changed.log'})
    logger.warn('warn message',         extra={'file_id':'changed.log'})
    logger.error('error message',       extra={'file_id':'changed.log'})
    logger.critical('critical message', extra={'file_id':'changed.log'})

【讨论】:

    【解决方案2】:

    听起来您正在寻找一个完全不同的日志系统——一个不维护任何全局状态的系统。你看过Logbook吗?

    或者,如果您必须避免第三方依赖,您可以使用logging.addLevelName 为每个请求添加一个级别,并添加一个带有filter 的处理程序,以删除每个不匹配的日志条目每个请求的记录器。一旦请求超出范围,您可以调用处理程序的 close 方法将其从树中删除。

    非常可能不会很好地扩展,因为每个处理程序的过滤器都会为每条日志消息调用。

    【讨论】:

      猜你喜欢
      • 2015-12-29
      • 1970-01-01
      • 2017-11-09
      • 1970-01-01
      • 2011-06-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-17
      相关资源
      最近更新 更多