python - python记录父子层次结构
问题描述
我试图通过首先创建一个 Father.Child 日志然后使用 logging.getLogger() 重新调用父亲日志来创建一个父/子日志记录层次结构,但由于某种原因我无法让它正常工作。下面是代码示例。在实际项目中,会有很多类将使用“clsLogger”创建一个 self.logger,每个类都会将所有类的日志写入同一个日志文件。
import logging
class clsLogger():
def __init__(self,LoggerName,Child=False,LoggerFileName='QpythonLog.txt'):
#create logger :
self.logger = logging.getLogger(LoggerName)
self.lvl = logging.DEBUG
self.logger.setLevel(self.lvl)
formatter = logging.Formatter('%(asctime)s %(name)s %(filename)s %(levelname)s: %(message)s')
#log to file :
self.filehandler = logging.FileHandler(LoggerFileName)
self.filehandler.setLevel = self.lvl
self.logger.addHandler(self.filehandler)
self.filehandler.setFormatter(formatter)
#log to console :
self.consoleHandler = logging.StreamHandler()
self.consoleHandler.setLevel(self.lvl)
self.logger.addHandler(self.consoleHandler)
self.consoleHandler.setFormatter(formatter)
log1 = clsLogger('Father.Child')
log1 = clsLogger('Father')
log1.logger.info('log from father')
log1 = clsLogger('Father.Child')
log1.logger.info('log from child')
输出(这是错误的)是:
2020-06-26 00:36:11,727 Father Services_TalynM_TalynA_v2.py INFO: log from father
2020-06-26 00:36:11,819 Father.Child Services_TalynM_TalynA_v2.py INFO: log from child
2020-06-26 00:36:11,819 Father.Child Services_TalynM_TalynA_v2.py INFO: log from child
2020-06-26 00:36:11,819 Father.Child Services_TalynM_TalynA_v2.py INFO: log from child
我真的希望它是:
2020-06-26 00:36:11,727 Father Services_TalynM_TalynA_v2.py INFO: log from father
2020-06-26 00:36:11,819 Father.Child Services_TalynM_TalynA_v2.py INFO: log from child
看起来每次我使用 logger.getLogger 它都会创建一个新的记录器对象,而不是使用第一个创建的 Father.Child 层次结构
解决方案
看起来每次我使用 logger.getLogger 它都会创建一个新的记录器对象,而不是使用第一个创建的 Father.Child 层次结构
当然不是。日志记录模块在内部字典 ( logging.Logger.manager.loggerDict
) 中注册每个记录器及其名称。根据定义,每个给定名称只能有一个记录器。根据logging
文档:
多次调用具有相同名称的 getLogger() 将始终返回对同一 Logger 对象的引用。
您可以在代码中验证这一点,如下所示:
<your code here>
# This is accessing an undocumented member; not safe for production code
print(logging.Logger.manager.loggerDict)
# Output:
# {'Father.Child': <Logger Father.Child (DEBUG)>, 'Father': <Logger Father (DEBUG)>}
问题是每次实例化clsLogger
类时,都会创建相同的处理程序并将它们附加到可能已经存在的记录器上。
分解它,这是:
log1 = clsLogger('Father.Child')
创建一个名为的记录器并将一个和一个Father.Child
附加到它。然后这个:FileHandler
StreamHandler
log1 = clsLogger('Father')
创建一个名为 的记录器Father
,该记录器根据名称成为其父级,Father.Child
并为其附加相同的处理程序。
下一行:
log1.logger.info('log from father')
向Father
记录器发送一条消息,将其发送给它的两个处理程序,因此该行:
2020-06-26 00:36:11,727 Father Services_TalynM_TalynA_v2.py INFO: log from father
在控制台和文件中。这一行:
log1 = clsLogger('Father.Child')
获取名为的现有记录器并将另一个和Father.Child
附加到它。所以你的最后一行:FileHandler
StreamHandler
log1.logger.info('log from child')
将消息发送到记录器的两个StreamHandler
和FileHandler
实例中的每一个Father.Child
,另外,由于Father
是父级Father.Child
并且您没有显式禁用传播,还将向其发送它的日志记录发送Father
到它自己的StreamHandler
和FileHandler
。这就是为什么你得到三倍的输出。
这可以像这样显示:
<your code here>
for lname, logger in logging.Logger.manager.loggerDict.items():
print(lname, logger.handlers, logger.parent)
# Output
# Father.Child [<FileHandler /home/shmee/.PyCharmCE2020.1/scratches/QpythonLog.txt (NOTSET)>, <StreamHandler <stderr> (DEBUG)>, <FileHandler /home/shmee/.PyCharmCE2020.1/scratches/QpythonLog.txt (NOTSET)>, <StreamHandler <stderr> (DEBUG)>] <Logger Father (DEBUG)>
# Father [<FileHandler /home/shmee/.PyCharmCE2020.1/scratches/QpythonLog.txt (NOTSET)>, <StreamHandler <stderr> (DEBUG)>] <RootLogger root (WARNING)>
顺便说一句:您设置的级别FileHandler
不正确:
self.filehandler.setLevel = self.lvl
因此NOTSET
这些处理程序的级别。您正确地为StreamHandler
:
self.consoleHandler.setLevel(self.lvl)
为了实现你想要的,你基本上会这样做:
class clsLogger():
def __init__(self,LoggerName,Child=False,LoggerFileName='QpythonLog.txt'):
#create logger :
self.logger = logging.getLogger(LoggerName)
self.lvl = logging.DEBUG
self.logger.setLevel(self.lvl)
formatter = logging.Formatter('%(asctime)s %(name)s %(filename)s %(levelname)s: %(message)s')
#log to file :
self.filehandler = logging.FileHandler(LoggerFileName)
self.filehandler.setLevel(self.lvl)
self.logger.addHandler(self.filehandler)
self.filehandler.setFormatter(formatter)
#log to console :
self.consoleHandler = logging.StreamHandler()
self.consoleHandler.setLevel(self.lvl)
self.logger.addHandler(self.consoleHandler)
self.consoleHandler.setFormatter(formatter)
self.logger.propagate = not Child
clsLogger('Father.Child', True)
clsLogger('Father')
logging.getLogger('Father').info('log from father')
logging.getLogger('Father.Child').info('log from child')
# Output
# 2020-06-26 00:36:11,727 Father Services_TalynM_TalynA_v2.py INFO: log from father
# 2020-06-26 00:36:11,819 Father.Child Services_TalynM_TalynA_v2.py INFO: log from child
但是,您的课程几乎是样板代码。正如文档所述(强调我的):
注意:如果将处理程序附加到记录器及其一个或多个祖先,它可能会多次发出相同的记录。通常,您不需要将处理程序附加到多个记录器- 如果您只是将其附加到记录器层次结构中最高的适当记录器,那么它将看到所有后代记录器记录的所有事件,前提是它们的传播设置保留为 True。一个常见的场景是仅将处理程序附加到根记录器,并让传播处理其余部分。
因此,使用隐式根记录器,以下将实现完全相同的目标,但在层次结构中或旁边使用附加记录器为您提供更大的灵活性:
logger = logging.getLogger()
handlers = [logging.FileHandler('QpythonLog.txt'), logging.StreamHandler()]
formatter = logging.Formatter('%(asctime)s %(name)s %(filename)s %(levelname)s: %(message)s')
[h.setFormatter(formatter) for h in handlers]
[logger.addHandler(h) for h in handlers]
[e.setLevel(logging.DEBUG) for e in (logger, *handlers)]
logging.getLogger('Father').info('log from father')
logging.getLogger('Father.Child').info('log from child')
logging.getLogger('Father.Child.GrandChild').info('log from grandchild')
logging.getLogger('sthElse').info('log from something else')
# Output
# 2020-06-26 01:45:37,617 Father frek.py INFO: log from father
# 2020-06-26 01:45:37,617 Father.Child frek.py INFO: log from child
# 2020-06-26 01:45:37,617 Father.Child.GrandChild frek.py INFO: log from grandchild
# 2020-06-26 01:45:37,617 sthElse frek.py INFO: log from something else
推荐阅读
- bash - 不能失败的竹建筑
- python - 如何在 Python 中比较两个字符串(英语除外)之间的相似性
- ruby - 编写一个程序,将所有从 `1` 到 `250`(含)的整数相加,然后将总和 `puts`
- excel - 根据数据表在新表上插入条目
- python - 如何在没有内存错误的情况下迭代熊猫数据帧
- c# - Azure Function Using Graph 我得到“无法加载文件或程序集'System.Text.Json”
- javascript - 如何在 Angular 中设置默认路由?
- sql - 为虚构的餐厅创建 SQL 数据库脚本
- angular - 如何验证打字稿中的多个选择框
- python - 我的熊猫数据框突然不会显示十进制数字?熊猫有什么变化吗?