使用Python日志记录模块时重复日志输出
我正在使用python记录器。以下是我的代码:使用Python日志记录模块时重复日志输出,python,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() h
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
调用获取多个条目。如何解决此问题?您多次调用Logger.myLogger()
。将它返回的记录器实例存储在某个位置,并重用该实例
还请注意,如果在添加任何处理程序之前登录,将创建默认的
StreamHandler(sys.stderr)
。您的记录器应作为单例运行。您不应该多次创建它。
下面是它的外观示例:
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")
logging.getLogger()
返回给定名称的相同实例。()
问题是每次调用myLogger()
,它都会向实例添加另一个处理程序,这会导致重复的日志
也许是这样的
导入操作系统
导入时间
导入日期时间
导入日志记录
记录器={}
def myLogger(名称):
全球伐木工人
如果是记录器,则获取(名称):
返回loggers.get(名称)
其他:
logger=logging.getLogger(名称)
logger.setLevel(logging.DEBUG)
now=datetime.datetime.now()
handler=logging.FileHandler(
“/root/credentials/Logs/provisionPython”
+now.strftime(“%Y-%m-%d”)
+“.log”)
格式化程序=logging.formatter('%(asctime)s%(levelname)s%(message)s')
handler.setFormatter(格式化程序)
logger.addHandler(处理程序)
记录器[名称]=记录器
返回记录器
给我开了个玩笑
使用Python2.7,logger的实现已经是单例的了 多次调用logging.getLogger('someLogger')返回一个引用 指向同一个记录器对象。这不仅在同一个国家是正确的 模块,也可以跨模块,只要它在同一个Python中 翻译过程。对同一对象的引用也是如此; 此外,应用程序代码可以定义和配置父级 在一个模块中创建(但不配置)子记录器 一个单独的模块,所有对该子模块的记录器调用将传递到 父母。这是一个主要模块 来源- 所以你应该用的方法是- 假设我们已经在主模块中创建并配置了一个名为“main_logger”的记录器(它只配置记录器,不返回任何内容) 现在在子模块中,如果我们按照命名层次结构“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
...
而且它也不会添加重复的处理程序
请参阅问题以获得更详细的答案。当您通过
importlib.reload
重新加载模块时,也可能会发生双(或三)或..-基于重新加载的次数)记录器输出(原因与已接受答案中解释的相同)。我添加这个答案只是为了将来参考,因为我花了一段时间才弄清楚为什么我的输出是双重(三重)定位的。一个简单的解决方法如下
logger.handlers[:] = [handler]
这样可以避免将新处理程序附加到基础列表“处理程序”中。您可以获得特定记录器的所有处理程序的列表,因此您可以执行以下操作
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)
在上面的示例中,我们检查指定文件的处理程序是否已连接到记录器,但通过访问所有处理程序的列表,您可以决定是否应添加其他处理程序。今天出现了此问题。因为我的函数是@staticmethod,所以上面的建议是用random()解决的 看起来像:
import random
logger = logging.getLogger('ProvisioningPython.{}'.format(random.random()))
由于Python3.2,您只需检查处理程序是否已经存在,如果已经存在,请在添加新处理程序之前清除它们。这在调试时非常方便,代码中包含记录器初始化
if (logger.hasHandlers()):
logger.handlers.clear()
logger.addHandler(handler)
这是对@rm957377答案的补充,但解释了发生这种情况的原因。当您在AWS中运行lambda函数时,它们会从包装实例中调用您的函数,该包装实例在多次调用时保持活动状态。也就是说,如果在函数的代码中调用
addHandler()
,每次函数运行时,它都会继续向日志单例添加重复的处理程序通过多次调用您的lambda函数,日志记录单例仍然存在。
要解决此问题,您可以在设置处理程序之前通过以下方式清除处理程序:
logging.getLogger().handlers.clear()
logging.getLogger().addHandler(...)
在大多数情况下,发生这种情况时,每个模块只需调用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')
然后两者都会有自己的完整包名和记录的方法。我已经使用了
记录器作为单例,并选中了如果不是len(logger.handlers)
,但是仍然得到了重复的:这是格式化的输出,后面是未格式化的输出
解决方案在我的情况下:
logger.propagate=False
归功于和。我在一个记录器中有3个处理程序
StreamHandler setLevel(args.logging_level)
logging.FileHandler(logging.ERROR)
RotatingFileHandler(args.logging_level)
logger.setLevel(args.logging_level)
我使用了我的代码
logger = logging.getLogger('same_name_everywhere')
产生的重复行和重复处理程序如下,2个流处理程序,3个旋转文件处理器
而1个流处理程序+2个旋转文件处理程序(1个用于errlog,1个用于一般日志)
这是由
logger.warn(logger.handlers)
cli_normalize_string: WARNING [<StreamHandler <stderr> (DEBUG)>, <RotatingFileHandler /tmp/cli.normalize_string.py.2020-11-02.user.errlog (ERROR)>, <StreamHandler <stderr> (DEBUG)>, <RotatingFileHandler /tmp/cli.normalize_string.py.2020-11-02.user.log (DEBUG)>, <RotatingFileHandler /tmp/cli.normalize_string.py.2020-11-02.user.errlog (ERROR)>]
在每个模块中,问题已解决,没有重复的行,1个StreamHeader,1个FileHandler用于错误日志记录,1个RotatingFileHandler用于一般日志记录
2020-11-02 21:26:05,856 cli_normalize_string INFO [<StreamHandler <stderr> (DEBUG)>, <FileHandler /tmp/cli.normalize_string.py.2020-11-02.user.errlog (ERROR)>, <RotatingFileHandler /tmp/cli.normalize_string.py.2020-11-02.user.log (DEBUG)>]
都是foo的后代。记录器名称层次结构类似于Python包层次结构,如果您进行组织,则与之相同
您的记录器基于每个模块,使用推荐的结构
那是因为在模块中
__name__
是Python包名称空间中的模块名称。适用于我。Python3.2和Windows
logger.warn(logger.handlers)
cli_normalize_string: WARNING [<StreamHandler <stderr> (DEBUG)>, <RotatingFileHandler /tmp/cli.normalize_string.py.2020-11-02.user.errlog (ERROR)>, <StreamHandler <stderr> (DEBUG)>, <RotatingFileHandler /tmp/cli.normalize_string.py.2020-11-02.user.log (DEBUG)>, <RotatingFileHandler /tmp/cli.normalize_string.py.2020-11-02.user.errlog (ERROR)>]
# The name is now become change.cli_normalize_string or change.normalize_string
logger = logger.getLogger(__name__)
2020-11-02 21:26:05,856 cli_normalize_string INFO [<StreamHandler <stderr> (DEBUG)>, <FileHandler /tmp/cli.normalize_string.py.2020-11-02.user.errlog (ERROR)>, <RotatingFileHandler /tmp/cli.normalize_string.py.2020-11-02.user.log (DEBUG)>]
foo.bar
foo.bar.baz
foo.bam
logging.getLogger(__name__).
__name__