Python日志记录模块多次打印行

时间:2013-07-19 12:18:48

标签: python logging

我有以下代码:

import logging
class A(object):
    def __init__(self):
        self._l = self._get_logger()

    def _get_logger(self):
        loglevel = logging.INFO
        l = logging.getLogger(__name__)
        l.setLevel(logging.INFO)
        h = logging.StreamHandler()
        f = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
        h.setFormatter(f)
        l.addHandler(h)
        l.setLevel(loglevel)
        return l  

    def p(self, msg):
        self._l.info(msg)

for msg in ["hey", "there"]:
    a = A()
    a.p(msg)

我得到的输出是:

2013-07-19 17:42:02,657 INFO hey
2013-07-19 17:42:02,657 INFO there
2013-07-19 17:42:02,657 INFO there

为什么“那里”被打印两次?同样,如果我在循环中添加A类的另一个对象并打印一条消息,它将被打印三次。

文档说如果记录器的名称匹配,logging.getLogger()将始终返回记录器的同一实例。在这种情况下,名称确实匹配。它不应该返回相同的记录器实例吗?如果它实际上是这样做的,为什么消息会被多次打印?

4 个答案:

答案 0 :(得分:16)

记录器创建一次,但会创建多个处理程序。

创建A一次。

a = A()
for msg in ["hey", "there"]:
    a.p(msg)

或按以下方式更改_get_logger

def _get_logger(self):
    loglevel = logging.INFO
    l = logging.getLogger(__name__)
    if not getattr(l, 'handler_set', None):
        l.setLevel(loglevel)
        h = logging.StreamHandler()
        f = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
        h.setFormatter(f)
        l.addHandler(h)
        l.setLevel(loglevel)
        l.handler_set = True
    return l  

<强>更新

从Python 3.2开始,您可以使用logging.Logger.hasHandlers查看此记录器是否配置了任何处理程序。 (感谢@toom)

def _get_logger(self):
    loglevel = logging.INFO
    l = logging.getLogger(__name__)
    if not l.hasHandlers():
        ...
    return l

答案 1 :(得分:7)

在我的例子中,也调用了root loggers处理程序,我所做的只是将logger实例的propagate属性设置为False

import logging
logger = logging.getLogger("MyLogger")

# stop propagting to root logger
logger.propagate = False

# other log configuration stuff
# ....

答案 2 :(得分:0)

自python 3.2及更高版本起:

考虑使用hasHandlers()检查记录器是否具有处理程序。

https://docs.python.org/3/library/logging.html#logging.Logger.hasHandlers

答案 3 :(得分:0)

最好在所有类和函数之外的模块级别上设置记录器,以避免重复设置处理程序。

对于不可避免的用例,请检查已附加到命名记录器的处理程序的数量,或者最好还是检查列表中该处理程序的存在。在Python 2.7.6中,Logger的类属性handlers是在Logger类实例上设置的处理程序的列表。不要附加列表中已经存在的处理程序。例如。

>>> import logging
>>> import logging.handlers # lib of log record handlers
>>> dir(logging.handlers)   # all the handlers from the lib
['BaseRotatingHandler', 'BufferingHandler', 'DEFAULT_HTTP_LOGGING_PORT', 'DEFAULT_SOAP_LOGGING_PORT', 'DEFAULT_TCP_LOGGING_PORT', 'DEFAULT_UDP_LOGGING_PORT', 'DatagramHandler', 'HTTPHandler', 'MemoryHandler', 'NTEventLogHandler', 'RotatingFileHandler', 'SMTPHandler', 'ST_DEV', 'ST_INO', 'ST_MTIME', 'SYSLOG_TCP_PORT', 'SYSLOG_UDP_PORT', 'SocketHandler', 'SysLogHandler', 'TimedRotatingFileHandler', 'WatchedFileHandler', '_MIDNIGHT', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '_unicode', 'cPickle', 'codecs', 'errno', 'logging', 'os', 're', 'socket', 'struct', 'time']
>>> l = logging.getLogger()   # root logger
>>> l.addHandler(logging.handlers.TimedRotatingFileHandler)
>>> l.addHandler(logging.handlers.WatchedFileHandler)
>>> l.handlers  # handlers set up on the logger instance
[<class 'logging.handlers.TimedRotatingFileHandler'>, <class 'logging.handlers.WatchedFileHandler'>]
>>> logging.handlers.TimedRotatingFileHandler in l.handlers # check - Yes
True
>>> logging.handlers.WatchedFileHandler in l.handlers # check - Yes
True
>>> logging.handlers.HTTPHandler in l.handlers # check - No
False
>>>