使用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
我遇到的问题是我在每个logger.info调用的日志文件中都有多个条目。 我怎么解决这个问题?
user865438 asked 2019-09-18T07:38:21Z
14个解决方案
73 votes
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
Werner Smit answered 2019-09-18T07:38:43Z
39 votes
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
Guillaume Cisco answered 2019-09-18T07:39:14Z
34 votes
从Python 3.2开始,您只需检查处理程序是否已存在,如果存在,请在添加新处理程序之前清除它们。 调试时,这非常方便,代码包括记录器初始化
if (logger.hasHandlers()):
logger.handlers.clear()
logger.addHandler(handler)
rm957377 answered 2019-09-18T07:39:38Z
9 votes
您不止一次致电StreamHandler(sys.stderr)。 存储它返回某处的logger实例并重用它。
另请注意,如果在添加任何处理程序之前进行日志记录,则将创建默认值StreamHandler(sys.stderr)。
Matt Joiner answered 2019-09-18T07:40:09Z
4 votes
记录器的实现已经是单例。
对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
...
它也不会添加重复的处理程序。
请看这个问题,以获得更详细的答案。
narayan answered 2019-09-18T07:41:22Z
3 votes
您的记录器应该像单身人士一样工作。 你不应该多次创建它。以下是它的外观示例:
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")
Zuljin answered 2019-09-18T07:41:46Z
2 votes
这是@ rm957377答案的补充,但解释了为什么会发生这种情况。 当您在AWS中运行lambda函数时,它们会在一个包含多个调用的包装实例中调用您的函数。 这意味着,如果在函数代码中调用addHandler(),则每次运行函数时,它将继续向日志单例添加重复处理程序。 记录单例通过多次调用lambda函数持续存在。
要解决此问题,您可以在设置之前清除处理程序:
logging.getLogger().handlers.clear()
logging.getLogger().addHandler(...)
Chad Befus answered 2019-09-18T07:42:18Z
1 votes
当您通过importlib.reload重新加载模块时,也可能发生双重(或三重或...... - 基于重新加载次数)记录器输出(出于与接受的答案中所述相同的原因)。 我正在添加这个答案只是为了将来的参考,因为我花了一段时间才弄清楚为什么我的输出是dupli(triple)cated。
rkuska answered 2019-09-18T07:42:43Z
0 votes
一个简单的解决方法就像
logger.handlers[:] = [handler]
这样您就可以避免将新处理程序附加到基础列表“处理程序”。
aihex answered 2019-09-18T07:43:14Z
0 votes
您可以获取特定记录器的所有处理程序列表,因此您可以执行此类操作
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)
在上面的示例中,我们检查指定文件的处理程序是否已挂接到记录器,但是有权访问所有处理程序的列表,这使您能够决定应该添加另一个处理程序的标准。
Most Wanted answered 2019-09-18T07:43:46Z
0 votes
今天有这个问题。 由于我的函数是@staticmethod,上面的建议用random()解决了。
看起来像:
import random
logger = logging.getLogger('ProvisioningPython.{}'.format(random.random()))
Pacman answered 2019-09-18T07:44:16Z
0 votes
大多数情况下,当发生这种情况时,只需要每个模块只调用一次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')
然后两者都将拥有自己的完整包名称和记录方法。
Harlin answered 2019-09-18T07:44:48Z
0 votes
我已经使用logger作为Singleton并检查了logger.propagate = False,但仍然有重复:这是格式化的输出,然后是未格式化的。
解决我的情况:logger.propagate = False
相信这个答案和文档。
Mr. B. answered 2019-09-18T07:45:27Z
-1 votes
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
Avinash Kumar answered 2019-09-18T07:45:45Z