27

我有以下代码:

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

4 回答 4

32

logger 只创建一次,但会创建多个处理程序。

创建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
于 2013-07-19T12:20:51.007 回答
17

在我的情况下,根记录器处理程序也被调用,我所做的只是将propagate记录器实例的属性设置为False

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

# stop propagting to root logger
logger.propagate = False

# other log configuration stuff
# ....
于 2016-05-20T07:37:12.773 回答
3

从 python 3.2 和更新版本开始:

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

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

于 2018-10-11T11:30:21.797 回答
2

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

对于不可避免的用例,请检查已附加到指定记录器的处理程序的数量,或者最好还是检查列表中是否存在处理程序。在 Python 2.7.6 中,Logger 的 class 属性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
>>> 
于 2019-04-22T07:27:20.690 回答