使用QueueHandler和QueueListener进行Python日志记录(logutils)

时间:2014-08-30 18:25:33

标签: python logging queue multiprocessing

多进程python应用程序需要进行日志记录。使用队列似乎是最好的解决方案。 logutils库提供了这个。

是否可以单独设置两个处理程序的日志级别?例如,在下面的测试中,我希望STREAM 1有WARNING消息而STREAM 2有INFO消息。在代码末尾的测试日志中,创建了一条INFO消息,该消息不应从STREAM 1处理程序(WARNING)输出到控制台。但是,它输出给两个处理程序。

作为参考,我一直在使用本页的作者Vinay Sajip的这个页面http://plumberjack.blogspot.co.uk/2010/09/improved-queuehandler-queuelistener.html

# System imports
import logging
import logging.handlers
try:
    import Queue as queue
except ImportError:
    import queue

# Custom imports
from logutils.queue import QueueHandler, QueueListener

# Get queue
q = queue.Queue(-1)

# Setup stream handler 1 to output WARNING to console
h1 = logging.StreamHandler()
f1 = logging.Formatter('STREAM 1 WARNING: %(threadName)s: %(message)s')
h1.setFormatter(f1)
h1.setLevel(logging.WARNING) # NOT WORKING. This should log >= WARNING

# Setup stream handler 2 to output INFO to console
h2 = logging.StreamHandler()
f2 = logging.Formatter('STREAM 2 INFO: %(threadName)s: %(message)s')
h2.setFormatter(f2)
h2.setLevel(logging.INFO) # NOT WORKING. This should log >= WARNING

# Start queue listener using the stream handler above
ql = QueueListener(q, h1, h2)
ql.start()

# Create log and set handler to queue handle
root = logging.getLogger()
root.setLevel(logging.DEBUG) # Log level = DEBUG
qh = QueueHandler(q)
root.addHandler(qh)

root.info('Look out!') # Create INFO message

ql.stop()

3 个答案:

答案 0 :(得分:1)

这是QueueListener.handle()方法实施的限制。目前这是:

def handle(self, record):
    record = self.prepare(record)
    for handler in self.handlers:
        handler.handle(record)

要做你想做的事,应该是

def handle(self, record):
    record = self.prepare(record)
    for handler in self.handlers:
        # CHANGED HERE TO ADD A CONDITION TO CHECK THE HANDLER LEVEL
        if record.levelno >= handler.level:
            handler.handle(record)

我会在某些时候解决这个问题,因为我认为这样做会更好,但是现在你可以继承QueueListener并覆盖子类中的handle方法。

答案 1 :(得分:1)

我使用此子类来覆盖队列侦听器类。另外两个方法addHandler和removeHandler允许添加和删除处理程序。 CustomQueueListener应该像QueueListener一样使用。请按照其他日志记录示例了解如何使用addHandler()removeHandler()

class CustomQueueListener(QueueListener):
    def __init__(self, queue, *handlers):
        super(CustomQueueListener, self).__init__(queue, *handlers)
        """
        Initialise an instance with the specified queue and
        handlers.
        """
        # Changing this to a list from tuple in the parent class
        self.handlers = list(handlers)

    def handle(self, record):
        """
        Override handle a record.

        This just loops through the handlers offering them the record
        to handle.

        :param record: The record to handle.
        """
        record = self.prepare(record)
        for handler in self.handlers:
            if record.levelno >= handler.level: # This check is not in the parent class
                handler.handle(record)

    def addHandler(self, hdlr):
        """
        Add the specified handler to this logger.
        """
        if not (hdlr in self.handlers):
            self.handlers.append(hdlr)

    def removeHandler(self, hdlr):
        """
        Remove the specified handler from this logger.
        """
        if hdlr in self.handlers:
            hdlr.close()
            self.handlers.remove(hdlr)

答案 2 :(得分:1)

您可以使用从Python 3.5版添加到respect_handler_levels初始化程序的QueueListener参数。

来自QueueListener's doc

  

如果respect_handler_levelTrue,则在决定是否将消息传递给该处理程序时,将遵守处理程序的级别(与消息的级别相比);否则,其行为与以前的Python版本相同-始终将每个消息传递给每个处理程序。

在您的情况下,应将QueueListener的初始化替换为:

ql = QueueListener(q, h1, h2, respect_handler_levels=True)