Python日志记录确保处理程序只添加一次

问题描述:

我有一段代码正在初始化一个记录器,如下所示。Python日志记录确保处理程序只添加一次

logger = logging.getLogger() 
hdlr = logging.FileHandler('logfile.log') 
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') 
hdlr.setFormatter(formatter) 
logger.addHandler(hdlr) 
logger.setLevel(logging.DEBUG) 

遗憾的是这段代码被称为多次,有没有什么办法,我可以检查,看看是否处理程序已经存在 - 我更愿意实现这个,而无需使用单例。

编辑:对不起,忘了提,这是蟒蛇2.5 - 欢呼声中,理查德

+0

我想你应该重新评估的答案,因为mouad的回答(在写作时)忽略了一个事实的方法,多次调用返回相同的记录器对象* CAN *添加重复处理。纳拉扬做了很好的解释。 – 2016-02-14 08:11:48

作为@offbyone注释,可以将冗余处理程序添加到记录器的同一个实例。 的python docs for logging声言如下─

“多给getLogger()具有相同的名称调用将返回一个 参照相同记录的对象。”

所以我们不需要担心使实现成为单例,因为它已经是了。

不幸的是,同样是不是真的为与相同的记录器实例相关的处理程序。有可以是附加的重复处理程序。

例 -

  1. 复制该代码,并将其保存在main.py

    import logging 
    print 'inside main.py', 
    print '-'*50 
    def logger(): 
    
         print 'initializing logger....' 
         logPath = '.' 
         fileName = 'temp' 
    
         # configure log formatter 
         logFormatter = logging.Formatter("%(asctime)s [%(filename)s] [%(funcName)s] [%(levelname)s] [%(lineno)d] %(message)s") 
    
         # configure file handler 
         fileHandler = logging.FileHandler("{0}/{1}.log".format(logPath, fileName)) 
         fileHandler.setFormatter(logFormatter) 
    
         # configure stream handler 
         consoleHandler = logging.StreamHandler() 
         consoleHandler.setFormatter(logFormatter) 
    
         # get the logger instance 
         logger = logging.getLogger(__name__) 
    
         # set the logging level 
         logger.setLevel(logging.DEBUG) 
    
         print 'adding handlers- ' 
    
         #if not len(logger.handlers): 
         logger.addHandler(fileHandler) 
         logger.addHandler(consoleHandler) 
    
         print 'logger initialized....\n' 
         print 'associated handlers - ', len(logger.handlers) 
         for handler in logger.handlers: 
          print handler 
         print 
         return logger 
    
    main_logger = logger() 
    main_logger.info('utilizing main.py logger.') 
    print 'exiting main.py', 
    print '-'*50 
    
  2. 和下面的代码sub.py

    print 'inside sub.py', 
    print '-'*50 
    print 'importing main.py' 
    import main 
    print 'imported main.py' 
    import logging 
    print 'getting logger instance in sub' 
    sub_logger = main.logger() 
    print 'got logger instance in sub' 
    sub_logger.info("utilizing sub_logger") 
    print 'exiting sub.py', 
    print '-'*50 
    
  3. 跑分。该方法的py

    [email protected]:~/code/so$ python sub.py 
    inside sub.py -------------------------------------------------- 
    importing main.py 
    inside main.py -------------------------------------------------- 
    initializing logger.... 
    adding handlers- 
    logger initialized.... 
    
    associated handlers - 2 
    <logging.FileHandler object at 0x7f7158740c90> 
    <logging.StreamHandler object at 0x7f7158710b10> 
    
    2015-08-04 07:41:01,824 [main.py] [<module>] [INFO] [41] utilizing main.py logger. 
    exiting main.py -------------------------------------------------- 
    imported main.py 
    getting logger instance in sub 
    initializing logger.... 
    adding handlers- 
    logger initialized.... 
    
    associated handlers - 4 # <===== 4 handlers (duplicates added) 
    <logging.FileHandler object at 0x7f7158740c90> 
    <logging.StreamHandler object at 0x7f7158710b10> 
    <logging.FileHandler object at 0x7f7158710bd0> 
    <logging.StreamHandler object at 0x7f7158710c10> 
    
    got logger instance in sub 
    2015-08-04 07:41:01,824 [sub.py] [<module>] [INFO] [10] utilizing sub_logger 
    2015-08-04 07:41:01,824 [sub.py] [<module>] [INFO] [10] utilizing sub_logger 
    exiting sub.py -------------------------------------------------- 
    

因此多个呼叫返回相同的记录器添加重复处理程序。现在

,为您question-

有什么办法,我可以检查,看看是否处理程序已经存在

是的,有是 -

logger.handlers返回列表所有与给定logger相关联的处理程序。

之前将处理程序记录器的实例,确保不添加重复处理 在main.py,只是取消注释,上面写着if not len(logger.handlers):行和缩进下面两行properly-

if not len(logger.handlers): 
    logger.addHandler(fileHandler) 
    logger.addHandler(consoleHandler) 

现在再次运行sub.py

[email protected]:~/code/so$ python sub.py 
inside sub.py -------------------------------------------------- 
importing main.py 
inside main.py -------------------------------------------------- 
initializing logger.... 
adding handlers- 
logger initialized.... 

associated handlers - 2 
<logging.FileHandler object at 0x7fd67a891c90> 
<logging.StreamHandler object at 0x7fd67a862b10> 

2015-08-04 08:14:45,620 [main.py] [<module>] [INFO] [41] utilizing main.py logger. 
exiting main.py -------------------------------------------------- 
imported main.py 
getting logger instance in sub 
initializing logger.... 
adding handlers- 
logger initialized.... 

associated handlers - 2 # <===== Still 2 handlers (no duplicates) 
<logging.FileHandler object at 0x7fd67a891c90> 
<logging.StreamHandler object at 0x7fd67a862b10> 

got logger instance in sub 
2015-08-04 08:14:45,620 [sub.py] [<module>] [INFO] [10] utilizing sub_logger 
exiting sub.py -------------------------------------------------- 

此外,如果你想限制要添加到记录器实例处理程序的类型,你可以不喜欢这个 -

print 'adding handlers- ' 
    # allows to add only one instance of file handler and stream handler 
    if len(logger.handlers) > 0: 
     print 'making sure we do not add duplicate handlers' 
     for handler in logger.handlers: 
       # add the handlers to the logger 
       # makes sure no duplicate handlers are added 

       if not isinstance(handler, logging.FileHandler) and not isinstance(handler, logging.StreamHandler): 
        logger.addHandler(fileHandler) 
        print 'added file handler' 
        logger.addHandler(consoleHandler) 
        print 'added stream handler' 
    else: 
     logger.addHandler(fileHandler) 
     logger.addHandler(consoleHandler) 
     print 'added handlers for the first time' 

希望这有助于!

编辑:

不幸的是,同样是不是真的与记录器的相同实例相关联 的处理程序。有可以重复 处理程序附加。

事实证明,上述说法并不完全正确。

假设我们已经在主模块中创建并配置了一个名为'main_logger'的记录器(它简单地配置记录器,不会返回任何东西)。

# get the logger instance 
logger = logging.getLogger("main_logger") 
# configuration follows 
... 

现在一个子模块,如果我们创建下列命名层次“main_logger.sub_module_logger”孩子记录仪,我们不需要配置它的子模块中。只需在命名层次结构中创建记录器就足够了。

# get the logger instance 
logger = logging.getLogger("main_logger.sub_module_logger") 
# no configuration needed 
# it inherits the configuration from the parent logger 
... 

而且它也不会添加重复处理程序。

参考 - Using logging in multiple modules

+1

这绝对是最好的答案。 mouad的答案不幸是短视的,因为它忽略了单个记录器实例(单例)的重复处理程序(而不是单例)的可能性。你的回答帮助我解决了我的问题。 – 2016-02-14 08:02:22

+0

“logger.handlers返回与给定记录器相关的所有处理程序的列表”...是的,但我可以问你,你是如何发现这一点的?你有没有回到源代码?因为python日志模块文档https://docs.python.org/2/library/logging.html没有显示这个信息。有时我觉得Python的标准文档有点缺乏......你可以推荐更好的东西吗? – 2016-02-20 14:45:06

+0

@mikerodent我在搜索结果中发现了有关_logger.handlers_的问题,当我在日志记录中遇到这个重复输出问题并且在源代码中验证了相同的结果时。关于文档,标准文档通常会完成这项工作。 [pymotw](https://pymotw.com/2/contents.html)可以帮助我,只要我想要在python标准库的帮助下完成特定的任务。 – narayan 2016-02-24 17:42:11

那么,如果处理程序已经存在logger.addHandler()不会添加处理程序。要检查处理器已经在那里你可以检查logger.handlers列表:

logger = logging.getLogger() 
hdlr = logging.FileHandler('logfile.log') 
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') 
hdlr.setFormatter(formatter) 
logger.addHandler(hdlr) 
logger.setLevel(logging.DEBUG) 
print logger.handlers 
# [<logging.FileHandler object at 0x14542d0>] 
logger.addHandler(hdlr) 
print logger.handlers 
# [<logging.FileHandler object at 0x14542d0>] 

除此之外,我会建议把这个代码在main()函数,如果你有一个或__init__.py文件你的包所以不必每次都调用它。我还建议你使用命名记录器,并且不要使用根记录器。事情是这样的:

logger = logging.getLogger(__name__) 
... 

希望这是有帮助:)

+2

是“Logger.handlers”列表记录在任何地方?我无法在http:// docs上找到它。python.org/2/library/logging.html – 2013-08-23 16:53:12

+1

@MariuszPluciński:是的,我不认为它记录在任何地方,但是如果我记得很好,我必须检查记录器类代码以了解如何工作:http:// hg。 python.org/cpython/file/482590320549/Lib/logging/__init__.py#l1215 – mouad 2013-08-26 09:10:05

+1

但是如果它没有记录,那么它可能会在未来版本的日志库中悄然消失。我不确定是否可以依赖这个功能(但我同意,似乎没有任何记录的方式来检查此功能)。 – 2013-08-26 17:38:08

尝试检查,如果logger已经设置。例如,如果此代码位于某个函数中:

logger = None 
def init_logger(): 
    global logger 
    if logger is not None: 
     #logger has already been initialized 
     return 
    logger = logging.getLogger() 
    hdlr = logging.FileHandler('logfile.log') 
    formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') 
    hdlr.setFormatter(formatter) 
    logger.addHandler(hdlr) 
    logger.setLevel(logging.DEBUG) 
+0

我想检查'如果记录器是不None'不是线程安全的,所以不能保证初始化代码将不会运行一次以上。 – 2012-03-27 22:25:49

您也可以检查处理程序列表是否为空。这里是我结束了与解决方案:

def setup_logging(self, logfile): 
    self._logger = logging.getLogger('TestSuite') 
    self._logger.setLevel(logging.INFO) 
    host = socket.gethostname().split('.')[0] 
    if self._logger.handlers == []: 
     fh = logging.handlers.RotatingFileHandler(logfile, 
                maxBytes=10*1024*1024, 
                backupCount=5) 
     strfmt = "%" + "(asctime)s [%s] " % host + "%" + "(message)s" 
     fmt = logging.Formatter(strfmt, datefmt="%Y.%m%d %H:%M:%S") 
     fh.setFormatter(fmt) 

     self._logger.addHandler(fh) 
    self._logger.info('-' * (55 - len(host))) 

我所看到的处理程序添加多次,所以每个日志消息是越来越写入日志文件多于一次,这固定它。