Python日志模块正在多次打印行

Python日志模块正在多次打印行,python,logging,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 = logg

我有以下代码:

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()将始终返回记录器的相同实例。在本例中,名称不匹配。它是否应该返回相同的记录器实例?如果确实是这样,为什么消息会被打印多次?

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

创建一次
A

a = A()
for msg in ["hey", "there"]:
    a.p(msg)
或更改
\u get\u 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以来,您可以使用查看此记录器是否配置了任何处理程序。(谢谢@toom)


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

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

# stop propagting to root logger
logger.propagate = False

# other log configuration stuff
# ....

自python 3.2及更新版本以来:

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


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

对于不可避免的用例,请检查已附加到命名记录器的处理程序的数量,或者更好地检查列表中是否存在该处理程序。在Python2.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
>>> 
导入日志记录 >>>导入logging.handlers#日志记录处理程序库 >>>dir(logging.handlers)#库中的所有处理程序 ['BaseRotatingHandler'、'BufferingHandler'、'DEFAULT_HTTP_LOGGING_PORT'、'DEFAULT_SOAP_LOGGING_PORT'、'DEFAULT_TCP_LOGGING_PORT'、'DEFAULT_UDP_LOGGING_PORT'、'DatagramHandler'、'HTTPHandler'、'MemoryHandler'、'NTEventLogHandler'、'RotatingFileHandler'、'sSMTPHandler'、'ST_DEV'、'ST_INO'、'ST'、'ST_MTIME'、'SYSLOG'、'sLogler、SysLogHandler、TimedRotatingFileHandler、WatchedFileHandler、午夜、内置、文档、文件、名称、包、unicode、cPickle、编解码器、错误号、日志、操作系统、套接字、结构、时间] >>>l=logging.getLogger()#根记录器 >>>l.addHandler(logging.handlers.TimedRotatingFileHandler) >>>l.addHandler(logging.handlers.WatchedFileHandler) >>>l.handlers#在记录器实例上设置的处理程序 [, ] >>>l.handlers中的logging.handlers.TimedRotatingFileHandler#选中-是 真的 >>>l.handlers中的logging.handlers.WatchedFileHandler#选中-是 真的 >>>l.handlers中的logging.handlers.HTTPHandler#检查-否 假的 >>>
我的用例不同。我希望能够生成实现自己的记录器的类的多个对象。这是正确的做事方式吗?还有,为什么日志行会被多次打印?@NikhilSingh:
getLogger
每次都返回相同的日志,但是每次实例化
A
,都是在同一个日志上调用
addHandler
。它将为您添加的每个处理程序打印一条消息。您将为每个实例构造一次记录器处理程序。您正在为“hey”和“there”创建一个实例,因此有两个处理程序。将行更改为
[“嘿”,“那里”,“巴迪”]
,您将获得3份“巴迪”。只创建每个类一次的A.OL日志记录器,而不是每个实例一次。因为Python 3.2:应该考虑<代码> L=日志记录。GETLogGER(γ-NAMEYSY)< /C> >然后>代码> L.HasHealLeSe()/<代码>方法。这就是工作@图姆,谢谢你提供的信息。我更新了答案以包含这些信息。没有注意到这个方法+1.
>>> 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
>>>