代码解析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模块的资料请关注脚本之家其它相关文章!

相关文章

  • 解读sqlalchemy的常用数据类型有哪些

    解读sqlalchemy的常用数据类型有哪些

    这篇文章主要介绍了解读sqlalchemy的常用数据类型有哪些,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03
  • python实现图像随机裁剪的示例代码

    python实现图像随机裁剪的示例代码

    这篇文章主要介绍了python实现图像随机裁剪的示例代码,帮助大家更好的理解和使用python处理图片,感兴趣的朋友可以了解下
    2020-12-12
  • Python使用RPC例子

    Python使用RPC例子

    本文主要介绍了Python使用RPC例子,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-06-06
  • Python 列表映射后的平均值

    Python 列表映射后的平均值

    这篇文章主要介绍了Python 列表映射后的平均值,下面文章将围绕Python 列表映射后的平均值得相关资料展开详细内容,需要的朋友可以参考一下
    2021-11-11
  • python进度条显示-tqmd模块的实现示例

    python进度条显示-tqmd模块的实现示例

    这篇文章主要介绍了python进度条显示-tqmd模块的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-08-08
  • python之django母板页面的使用

    python之django母板页面的使用

    这篇文章主要介绍了python之django母板页面的使用,母版页用于处理html页面相同部分内容,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-07-07
  • 常见python正则用法的简单实例

    常见python正则用法的简单实例

    下面小编就为大家带来一篇常见python正则用法的简单实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-06-06
  • python logging.info在终端没输出的解决

    python logging.info在终端没输出的解决

    这篇文章主要介绍了python logging.info在终端没输出的解决,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-05-05
  • 解决python 文本过滤和清理问题

    解决python 文本过滤和清理问题

    文本过滤和清理所涵盖的范围非常广泛,涉及文本解析和数据处理方面的问题。这篇文章主要介绍了解决python 文本过滤和清理问题,需要的朋友可以参考下
    2019-08-08
  • Python实现 MK检验示例代码

    Python实现 MK检验示例代码

    这篇文章主要介绍了Python实现 MK检验,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-12-12

最新评论