问题:使用Python日志记录模块时重复的日志输出
我正在使用python记录器。以下是我的代码:
import os
import time
import datetime
import logging
class Logger :
def myLogger(self):
logger = logging.getLogger('ProvisioningPython')
logger.setLevel(logging.DEBUG)
now = datetime.datetime.now()
handler=logging.FileHandler('/root/credentials/Logs/ProvisioningPython'+ now.strftime("%Y-%m-%d") +'.log')
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
return logger
我的问题是我在每个logger.info
呼叫的日志文件中都有多个条目。我该如何解决?
回答 0
该logging.getLogger()
已经是一个单例。(资料)
问题在于,每次调用时myLogger()
,都会向实例添加另一个处理程序,这将导致日志重复。
也许是这样的?
import os
import time
import datetime
import logging
loggers = {}
def myLogger(name):
global loggers
if loggers.get(name):
return loggers.get(name)
else:
logger = logging.getLogger(name)
logger.setLevel(logging.DEBUG)
now = datetime.datetime.now()
handler = logging.FileHandler(
'/root/credentials/Logs/ProvisioningPython'
+ now.strftime("%Y-%m-%d")
+ '.log')
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
loggers[name] = logger
return logger
回答 1
从Python 3.2开始,您可以检查是否已经存在处理程序,如果存在,请在添加新处理程序之前清除它们。这在调试时非常方便,并且代码包括记录器的初始化
if (logger.hasHandlers()):
logger.handlers.clear()
logger.addHandler(handler)
回答 2
import datetime
import logging
class Logger :
def myLogger(self):
logger=logging.getLogger('ProvisioningPython')
if not len(logger.handlers):
logger.setLevel(logging.DEBUG)
now = datetime.datetime.now()
handler=logging.FileHandler('/root/credentials/Logs/ProvisioningPython'+ now.strftime("%Y-%m-%d") +'.log')
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
return logger
帮了我大忙
使用python 2.7
回答 3
我已经将它logger
用作Singleton并进行了检查if not len(logger.handlers)
,但仍然有重复项:它是格式化的输出,然后是未格式化的输出。
我的解决方案:
logger.propagate = False
回答 4
您拨打Logger.myLogger()
了不止一次。存储返回某处记录器实例和重用那。
还建议如果在添加任何处理程序之前登录,StreamHandler(sys.stderr)
将创建默认值。
回答 5
这是@ rm957377答案的补充,但解释了为什么发生这种情况。当您在AWS中运行lambda函数时,它们从包装实例中调用您的函数,该包装实例对于多个调用仍然有效。这意味着,如果您addHandler()
在函数的代码内调用,它将在每次函数运行时继续向日志记录单例添加重复的处理程序。 日志单例通过lambda函数的多次调用而持续存在。
为了解决这个问题,您可以在设置处理器之前先清除它们,方法是:
logging.getLogger().handlers.clear()
logging.getLogger().addHandler(...)
回答 6
您的记录器应作为单例工作。您不应该多次创建它。这是看起来的示例:
import os
import time
import datetime
import logging
class Logger :
logger = None
def myLogger(self):
if None == self.logger:
self.logger=logging.getLogger('ProvisioningPython')
self.logger.setLevel(logging.DEBUG)
now = datetime.datetime.now()
handler=logging.FileHandler('ProvisioningPython'+ now.strftime("%Y-%m-%d") +'.log')
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
handler.setFormatter(formatter)
self.logger.addHandler(handler)
return self.logger
s = Logger()
m = s.myLogger()
m2 = s.myLogger()
m.info("Info1")
m2.info("info2")
回答 7
logger的实现已经是单例。
多次调用logging.getLogger(’someLogger’)会返回对同一记录器对象的引用。只要在同一Python解释器进程中,不仅在同一模块内,而且在各个模块之间都是如此。对于相同对象的引用是正确的。此外,应用程序代码可以在一个模块中定义和配置父记录器,并在单独的模块中创建(但不能配置)子记录器,并且所有对子记录器的调用都将传递给父记录器。这是一个主要模块
来源- 在多个模块中使用日志记录
因此,您应该利用它的方式是-
假设我们已经在主模块中创建并配置了一个名为“ 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
...
而且它也不会添加重复的处理程序。
见这对一个小更详细的回答问题。
回答 8
当您通过方式重新加载模块时,也会发生两倍(或三倍或..-基于重新加载的次数)记录器输出importlib.reload
(出于与接受的答案中所述相同的原因)。我添加此答案只是为了将来参考,因为花了我一段时间才弄清楚为什么我的输出是dupli(triple)cated。
回答 9
一种简单的解决方法是
logger.handlers[:] = [handler]
这样,您可以避免将新的处理程序附加到基础列表“处理程序”。
回答 10
在大多数情况下,这种情况的底线是,每个模块只需要调用一次logger.getLogger()。如果像我一样有多个类,我可以这样称呼它:
LOGGER = logger.getLogger(__name__)
class MyClass1:
log = LOGGER
def __init__(self):
self.log.debug('class 1 initialized')
class MyClass2:
log = LOGGER
def __init__(self):
self.log.debug('class 2 initialized')
然后两者都将在记录的地方拥有自己的完整程序包名称和方法。
回答 11
您可以获取特定记录器的所有处理程序的列表,因此您可以执行以下操作
logger = logging.getLogger(logger_name)
handler_installed = False
for handler in logger:
# Here your condition to check for handler presence
if isinstance(handler, logging.FileHandler) and handler.baseFilename == log_filename:
handler_installed = True
break
if not handler_installed:
logger.addHandler(your_handler)
在上面的示例中,我们检查指定文件的处理程序是否已经连接到记录器,但是可以访问所有处理程序的列表,使您能够决定应添加哪个处理程序或不添加其他处理程序。
回答 12
今天有这个问题。由于我的函数是@staticmethod,因此上述建议已通过random()解决。
看起来像:
import random
logger = logging.getLogger('ProvisioningPython.{}'.format(random.random()))
回答 13
from logging.handlers import RotatingFileHandler
import logging
import datetime
# stores all the existing loggers
loggers = {}
def get_logger(name):
# if a logger exists, return that logger, else create a new one
global loggers
if name in loggers.keys():
return loggers[name]
else:
logger = logging.getLogger(name)
logger.setLevel(logging.DEBUG)
now = datetime.datetime.now()
handler = logging.FileHandler(
'path_of_your_log_file'
+ now.strftime("%Y-%m-%d")
+ '.log')
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
loggers.update(dict(name=logger))
return logger