本文小编为大家详细介绍“python标准库logging模块怎么用”,内容详细,步骤清晰,细节处理妥当,希望这篇“python标准库logging模块怎么用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。
问题1:如何获取caller的(文件名,行号,函数名)?
当新增一条log记录时,最终将调用Logger类的_log方法,这个方法首先会创建一个LogRecord对象。LogRecord对象需要(filename, lineno, funcname)参数信息。这是通过如下语句得到的:
fn, lno, func = self.findCaller()
findCaller内容如下:
f = currentframe() #f是frame对象,每个方法调用生成一个frame对象,放在程序堆栈中。 if f is not None: f = f.f_back rv = "(unknown file)", 0, "(unknown function)" while hasattr(f, "f_code"): co = f.f_code #获取code对象,它包含filename属性,funcname属性 filename = os.path.normcase(co.co_filename) if filename == _srcfile: #_srcfile是这个模块文件自己的文件名,当文件名不再相同时 f = f.f_back # 得到外部调用者的frame,这就是需要的。 continue rv = (filename, f.f_lineno, co.co_name) break return rv
currentframe函数的定义:
def currentframe(): """Return the frame object for the caller's stack frame.""" try: raise Exception #抛出异常,将生成traceback对象,其中包含frame对象。 except: #sys.exc_traceback.tb_frame当前的frame, f_back调用着的frame return sys.exc_traceback.tb_frame.f_back#sys._getframe(3)返回的并不是当前的frame,3应该是计算好了的,减少循环的次数,返回的是logger.error()的frameif hasattr(sys, '_getframe'): currentframe = lambda: sys._getframe(3)
问题2: Logger对象的层级,父子关系如何实现的?
首先,logging模块中logger层级关系是一个树形关系的结构,这个关系的树根是'root'。
root = RootLogger(WARNING) #RootLogger类是Logger的子类,无特殊功能,只是定义名字为‘root'。Logger.root = rootLogger.manager = Manager(Logger.root)
当调用logging.getLogger(),用以获取某个Logger时,如果参数为空,则返回‘root’。否则,调用Manager的getLogger()方法获取Logger。
def getLogger(name=None): """ Return a logger with the specified name, creating it if necessary. If no name is specified, return the root logger. """ if name: return Logger.manager.getLogger(name) else: return root
Manager的getLogger()定义如下:
def getLogger(self, name): """ Get a logger with the specified name (channel name), creating it if it doesn't yet exist. This name is a dot-separated hierarchical name, such as "a", "a.b", "a.b.c" or similar. If a PlaceHolder existed for the specified name [i.e. the logger didn't exist but a child of it did], replace it with the created logger and fix up the parent/child references which pointed to the placeholder to now point to the logger. """ rv = None _acquireLock() try: if name in self.loggerDict: rv = self.loggerDict[name] if isinstance(rv, PlaceHolder): ph = rv rv = _loggerClass(name) rv.manager = self self.loggerDict[name] = rv self._fixupChildren(ph, rv) self._fixupParents(rv) else: rv = _loggerClass(name) rv.manager = self self.loggerDict[name] = rv self._fixupParents(rv) finally: _releaseLock() return rv
Manager对象中的loggerDict字典,存放logger名字和logger对象的映射关系。PlaceHolder类,是一个容器。
例如,名字为'sell'的PlaceHolder对象,首先还不存在'sell'的logger,然后,所以以'sell‘开头的logger在这个对象内都存在一个引用,如'sell.food','sell.cloth.china'等已有的logger对象。 当调用getLogger()获取一个未存在的logger时,如名字为'level1.level2', 首先创建一个名字为'level1.level2'的logger对象,并存于loggerDict中。然后,调用_fixupParents()。
_fixupParents()的作用:
在这个名字的层级链上,找到第一个logger对象,将其作为父亲,并返回。链上不是logger对象的名字,创建一个PlaceHolder对象(如果未创建),将自己加入其中。
例如,新增‘level1.level2.level3’的logger,调用_fixupParents将创建一个名字为'level1.level2‘的PlaceHolder对象,创建一个名字为’level1‘的PlaceHolder对象,并将’level1.level2.level3‘这个logger分别加入以上两个PlaceHolder对象容器内,将它的父亲设定为’root‘。
总之,_fixupParents是使logger对象指向真正的父亲节点(logger对象),并将logger自己加入到所有上层的PlaceHolder对象容器内。
如果获取一个名字已存在于loggerDict中,并且这个名字对应的是一个先前创建的PlaceHolder对象。首先,创建一个对应名字的logger对象。然后,调用_fixupChild(),修正这个PlaceHolder对象所包含的下游logger对象的父亲。最后,调用_fixupParent(),作用与上一步相同。
父子层级关系,主要作用是,当logger对象的propagate属性值1(默认值)时,每条logRecord记录都会传给父logger处理。这样可以只需要定义好‘root’根logger对象,其他的logger定义个名字,根据模块名,类名等,然后绑定一个NullHandler。最后,所有的logRecord将交给’root‘统一处理处理。这是多模块产生统一格式log的方式。
读到这里,这篇“python标准库logging模块怎么用”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注编程网行业资讯频道。