Dan*_*pir 1 python logging hierarchy
我试图通过首先创建 Father.Child 日志,然后使用logging.getLogger() 重新调用Father log 来创建Father/Child 日志记录层次结构,但由于某种原因,我无法让它正常工作。下面是代码示例。在实际项目中,会有很多类使用“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')
Run Code Online (Sandbox Code Playgroud)
输出(这是错误的)是:
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
Run Code Online (Sandbox Code Playgroud)
我真正希望它是:
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
Run Code Online (Sandbox Code Playgroud)
看起来每次我使用 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)>}
Run Code Online (Sandbox Code Playgroud)
问题是,每次实例化clsLogger类时,您都会创建相同的处理程序并将它们附加到可能已经存在的记录器。
分解一下,这个:
log1 = clsLogger('Father.Child')
Run Code Online (Sandbox Code Playgroud)
创建一个名为 loggerFather.Child并将一FileHandler和一附加StreamHandler到它。然后这个:
log1 = clsLogger('Father')
Run Code Online (Sandbox Code Playgroud)
创建一个名为 的记录器,该记录器根据名称Father成为 的父级,并向其附加相同的处理程序。
下一行:Father.Child
log1.logger.info('log from father')
Run Code Online (Sandbox Code Playgroud)
向记录器发送一条消息Father,记录器将其发送到其两个处理程序,因此该行:
2020-06-26 00:36:11,727 Father Services_TalynM_TalynA_v2.py INFO: log from father
Run Code Online (Sandbox Code Playgroud)
在控制台和文件中。这行:
log1 = clsLogger('Father.Child')
Run Code Online (Sandbox Code Playgroud)
获取名为的现有Father.Child记录器并将另一个记录器附加FileHandler到StreamHandler它。所以你的最后一行:
log1.logger.info('log from child')
Run Code Online (Sandbox Code Playgroud)
将消息发送到记录器的两个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)>
Run Code Online (Sandbox Code Playgroud)
顺便说一句:您设置的级别不FileHandler正确:
self.filehandler.setLevel = self.lvl
Run Code Online (Sandbox Code Playgroud)
因此这些处理程序的级别NOTSET。您正确地执行了以下操作StreamHandler:
self.consoleHandler.setLevel(self.lvl)
Run Code Online (Sandbox Code Playgroud)
为了实现你想要的,你基本上会这样做:
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
Run Code Online (Sandbox Code Playgroud)
但是,您的类几乎是样板代码。正如文档所述(强调我的):
注意:如果将处理程序附加到记录器及其一个或多个祖先,它可能会多次发出相同的记录。一般来说,您不需要将处理程序附加到多个记录器- 如果您只需将其附加到记录器层次结构中最高的相应记录器,那么它将看到所有后代记录器记录的所有事件,前提是它们传播设置保留为 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
Run Code Online (Sandbox Code Playgroud)