是否可以使用单个记录器编写许多不同的日志文件?

时间:2013-04-04 22:38:11

标签: python logging

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

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

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

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

这可能吗?

2 个答案:

答案 0 :(得分:2)

查看RotatingFileHandlerlogging.handlers的代码最终给了我足够的线索来解决这个问题。关键的实现是,在记录消息时,可以传递可选的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'})

答案 1 :(得分:0)

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

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

然而,非常可能无法很好地扩展,因为每个日志消息都将调用每个处理程序的过滤器。