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

我正在使用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
调用获取多个条目。如何解决此问题?

您多次调用
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__