Python日志(logutils)与QueueHandler和QueueListener
在一个多进程的Python应用程序中,需要进行日志记录。使用队列似乎是最好的解决方案,而logutils库正好提供了这个功能。
我想知道是否可以为两个处理器独立设置日志级别?比如,在下面的测试中,我希望STREAM 1只显示WARNING级别的消息,而STREAM 2显示INFO级别的消息。在代码最后的测试日志中,生成了一条INFO级别的消息,但我希望这条消息不会通过STREAM 1的处理器输出(因为它只应该显示WARNING级别的)。然而,它却同时输出到了两个处理器。
作为参考,我一直在使用这篇文章 http://plumberjack.blogspot.co.uk/2010/09/improved-queuehandler-queuelistener.html,作者是这个库的Vinay Sajip。
# 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 个回答
我用这个子类来重写队列监听器类。还有两个方法,分别是 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)
你可以使用从Python 3.5版本开始新增的respect_handler_level
参数,这样监听器就会尊重每个处理器的级别。
如果
respect_handler_level
设置为True
,那么在决定是否将消息传递给某个处理器时,会考虑该处理器的级别(与消息的级别进行比较);否则,行为就和之前的Python版本一样——每条消息都会传递给每个处理器。
在你的情况下,你应该将QueueListener
的初始化替换为:
ql = QueueListener(q, h1, h2, respect_handler_level=True)
这是在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
方法。