我有以下代码:
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()将始终返回记录器的同一实例。在这种情况下,名称确实匹配。它不应该返回相同的记录器实例吗?如果它实际上是这样做的,为什么消息会被多次打印?
答案 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
>>>