使用Python日志记录模块时,日志输出重复

use*_*438 89 python

我正在使用python logger.以下是我的代码:

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
Run Code Online (Sandbox Code Playgroud)

我遇到的问题是我在每个logger.info调用的日志文件中都有多个条目.我怎么解决这个问题?

Wer*_*mit 83

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
Run Code Online (Sandbox Code Playgroud)

  • 我认为你应该有loggers.update(dict((name,logger))). (3认同)
  • 全局“loggers”字典与“logging.getLogger”不是多余的吗?因为您真的只是想避免添加额外的处理程序,所以您似乎更喜欢下面的答案来直接检查处理程序 (2认同)

Gui*_*sco 43

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
Run Code Online (Sandbox Code Playgroud)

为我做了伎俩

使用python 2.7

  • 感谢您的提示,BTW检查列表是否为空您不需要使用"len"运算符,您可以直接使用如果my_list:.. (3认同)

小智 40

从Python 3.2开始,您只需检查处理程序是否已存在,如果存在,请在添加新处理程序之前清除它们.调试时,这非常方便,代码包括记录器初始化

if (logger.hasHandlers()):
    logger.handlers.clear()

logger.addHandler(handler)
Run Code Online (Sandbox Code Playgroud)

  • 请注意,即使尚未添加本地/自定义处理程序,在已将处理程序添加到根记录器的pytest中,hasHandlers()也会返回true。在这种情况下,len(logger.handlers)(根据Guillaume的回答)将返回0,因此可能是一个更好的选择。 (2认同)

Mat*_*ner 9

Logger.myLogger()不止一次打电话.存储它返回某处的logger实例并重用.

另请注意,如果在添加任何处理程序之前进行日志记录,StreamHandler(sys.stderr)则会创建默认值.


Mr.*_* B. 8

我已经将它logger用作Singleton并进行了检查if not len(logger.handlers),但仍然有重复项:它是格式化的输出,然后是未格式化的输出。

我的解决方案logger.propagate = False

归功于这个答案文档

  • 我发现双重日志记录来自 RootLogger 和我的 StreamHandler,但在执行此操作之前无法解决问题(同时将格式化程序保留在 StreamHandler 上)。 (2认同)

小智 8

问题是处理程序的数量,如果您有多个处理程序,则您有多个日志,因此您只需在添加之前进行检查:

if not logger.handlers:
    logger.addHandler(handler)
Run Code Online (Sandbox Code Playgroud)


nar*_*yan 6

logger 的实现已经是单例了。

多次调用 logging.getLogger('someLogger') 返回对同一个记录器对象的引用。这不仅在同一个模块内如此,而且在跨模块时也是如此,只要它在同一个 Python 解释器进程中。对同一对象的引用也是如此;此外,应用程序代码可以在一个模块中定义和配置父记录器,并在单独的模块中创建(但不配置)子记录器,所有对子记录器的调用都将传递给父记录器。这是一个主要模块

源 -在多个模块中使用日志记录

所以你应该使用它的方式是 -

假设我们已经在主模块中创建并配置了一个名为“main_logger”的记录器(它只是配置记录器,不返回任何内容)。

# get the logger instance
logger = logging.getLogger("main_logger")
# configuration follows
...
Run Code Online (Sandbox Code Playgroud)

现在在子模块中,如果我们按照命名层次结构'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
...
Run Code Online (Sandbox Code Playgroud)

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

请参阅问题以获得更详细的答案。


Cha*_*fus 6

这是对@rm957377 的回答的补充,但解释了为什么会发生这种情况。当您在 AWS 中运行 lambda 函数时,它们会从一个为多次调用而保持活动状态的包装实例中调用您的函数。意思是,如果您addHandler()在函数的代码中调用,则每次函数运行时,它都会继续向日志记录单例添加重复的处理程序。 日志记录单例通过多次调用 lambda 函数而持续存在。

要解决此问题,您可以在通过以下方式设置处理程序之前清除它们:

logging.getLogger().handlers.clear()
logging.getLogger().addHandler(...)
Run Code Online (Sandbox Code Playgroud)


Zul*_*jin 5

你的记录器应该作为单例工作。您不应多次创建它。下面是它的外观示例:

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")
Run Code Online (Sandbox Code Playgroud)


Sur*_*mar 5

当我们使用不带任何参数的 getLogger() 时,它返回 RootLogger。

因此,如果您在多个位置调用 getLogger() 并添加日志处理程序,它会将这些日志处理程序添加到 RootLogger(如果您不显式添加日志处理程序,它将自动添加 StreamHandler)。因此,当您尝试记录消息时,它将使用添加到 RootLogger 的所有处理程序来记录消息。这就是重复日志的原因。

您可以通过在调用 getLogger() 时提供不同的记录器名称来避免这种情况。就像是

logger1 = logging.getLogger("loggera")

logger2 = logging.getLogger("loggerb")
Run Code Online (Sandbox Code Playgroud)

这对我有用。