简体   繁体   English

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

[英]Python logging module is printing lines multiple times

I have the following code:我有以下代码:

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)

The output that I get is:我得到的输出是:

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

Why is "there" being printed twice?为什么“那里”被打印两次? Similarly, if I add another object of class A inside the loop and print a message, it gets printed thrice.同样,如果我在循环中添加另一个 A 类对象并打印一条消息,它会被打印三次。

The documentation says that logging.getLogger() will always return the same instance of the logger if the name of the logger matches.该文档说,如果记录器的名称匹配, logging.getLogger() 将始终返回记录器的相同实例。 In this case, the name does match.在这种情况下,名称确实匹配。 Should it not return the same logger instance?它不应该返回相同的记录器实例吗? If it is infact doing so, why is the message getting printed multiple times?如果确实如此,为什么会多次打印消息?

logger is created once, but multiple handlers are created. logger 被创建一次,但会创建多个处理程序。

Create A once.创建A一次。

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

Or change _get_logger as follow:或更改_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  

UPDATE更新

Since Python 3.2, you can use logging.Logger.hasHandlers to see if this logger has any handlers configured.从 Python 3.2 开始,您可以使用logging.Logger.hasHandlers来查看此记录器是否配置了任何处理程序。 (thanks @toom) (感谢@toom)

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

In my case , the root loggers handler were also being called , All I did was to set propagate attribute of logger instance to False .就我而言,根记录器处理程序也被调用,我所做的只是将记录器实例的propagate属性设置为False

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

# stop propagting to root logger
logger.propagate = False

# other log configuration stuff
# ....

Since python 3.2 and newer:从 python 3.2 及更新版本开始:

Consider using hasHandlers() to check if a logger has handlers or not.考虑使用hasHandlers()来检查记录器是否有处理程序。

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

Best to set up the logger at the module level outside all classes and functions to avoid having it repeatedly setting up the handler.最好在所有类和函数之外的模块级别设置记录器,以避免重复设置处理程序。

For use cases where that's unavoidable, check the number of handlers already attached to the named logger, or better still check presence of the handler in the list.对于不可避免的用例,请检查已附加到命名记录器的处理程序的数量,或者最好仍然检查列表中是否存在处理程序。 In Python 2.7.6, Logger's class attribute handlers is a list of the handlers set up on the Logger class instance.在 Python 2.7.6 中,Logger 的类属性handlers是在 Logger 类实例上设置的处理程序列表。 Don't attach a handler that's already in the list.不要附加已经在列表中的处理程序。 Eg例如

>>> 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
>>> 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM