代码解析python标准库logging模块

 更新时间:2022年05月20日 11:34:42   作者:wangst4321  
这篇文章主要为大家介绍了代码解析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()的frame
if hasattr(sys, '_getframe'): currentframe = lambda: sys._getframe(3)

问题2: Logger对象的层级,父子关系如何实现的?

首先,logging模块中logger层级关系是一个树形关系的结构,这个关系的树根是'root'。

root = RootLogger(WARNING)     #RootLogger类是Logger的子类,无特殊功能,只是定义名字为‘root'。
Logger.root = root
Logger.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模块的详细内容,更多关于python标准库logging模块的资料请关注脚本之家其它相关文章!

相关文章

  • Python实现mysql数据库更新表数据接口的功能

    Python实现mysql数据库更新表数据接口的功能

    这篇文章主要给大家介绍了关于Python如何实现mysql数据库更新表数据接口功能的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2017-11-11
  • Python实现将内容写入文件的五种方法总结

    Python实现将内容写入文件的五种方法总结

    本篇带你详细看一下python将内容写入文件的方法以及细节,主要包括write()方法、writelines() 方法、print() 函数、使用 csv 模块、使用 json 模块,需要的可以参考一下
    2023-04-04
  • 举例讲解Python常用模块

    举例讲解Python常用模块

    今天小编就为大家分享一篇关于举例讲解Python常用模块,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-03-03
  • 详解Python中静态类型语言的写法

    详解Python中静态类型语言的写法

    众所周知,python是一种动态类型语言,但是,从v3.6版本开始,开始支持静态类型的写法,下面小编就来和大家聊聊Python中静态类型语言的具体写法吧
    2023-07-07
  • Python进阶之路模块、包和异常处理详解

    Python进阶之路模块、包和异常处理详解

    这篇文章主要介绍了Python进阶之路模块、包和异常处理的相关资料,文中通过实例演示,展示了如何构建模块化的应用并遵循最佳实践,需要的朋友可以参考下
    2024-12-12
  • Python常用随机数与随机字符串方法实例

    Python常用随机数与随机字符串方法实例

    这篇文章主要介绍了Python常用随机数与随机字符串方法实例,本文讲解了随机整数、随机选取0到100间的偶数、随机浮点数、随机字符串等常用随机方法,需要的朋友可以参考下
    2015-04-04
  • pyqt5 从本地选择图片 并显示在label上的实例

    pyqt5 从本地选择图片 并显示在label上的实例

    今天小编就为大家分享一篇pyqt5 从本地选择图片 并显示在label上的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-06-06
  • Python PyQt5实战项目之文件拷贝器的具体实现详解

    Python PyQt5实战项目之文件拷贝器的具体实现详解

    PyQt5以一套Python模块的形式来实现功能。它包含了超过620个类,600个方法和函数。本篇文章手把手带你用PyQt5实现一个简单的文件拷贝器,大家可以在过程中查缺补漏,提升水平
    2021-11-11
  • 详解Python 4.0 预计推出的新功能

    详解Python 4.0 预计推出的新功能

    这篇文章主要介绍了详解Python 4.0 预计推出的新功能,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-07-07
  • Python算法绘制特洛伊小行星群实现示例

    Python算法绘制特洛伊小行星群实现示例

    这篇文章主要介绍了Python算法绘制特洛伊小行星群实现示例,这个小示例完成后非常的有意思也会使你在Python学习的道路上感到一丝丝小成就
    2021-10-10

最新评论