代码解析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 sys模块使用方法介绍

    python sys模块使用方法介绍

    sys模块功能多,我们这里介绍一些比较实用的功能,相信你会喜欢的,和我一起走进python的模块吧
    2023-01-01
  • Python如何写入Pandas DataFrame到CSV文件

    Python如何写入Pandas DataFrame到CSV文件

    Pandas是一个功能强大的Python数据分析库,常用于处理和分析数据,CSV文件是一种广泛使用的数据交换格式,Pandas通过to_csv方法支持将DataFrame写入CSV文件,此方法允许用户指定分隔符、编码和选择性写入特定列等
    2024-09-09
  • 浅谈python字典多键值及重复键值的使用

    浅谈python字典多键值及重复键值的使用

    下面小编就为大家带来一篇浅谈python字典多键值及重复键值的使用。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-11-11
  • python实现学员管理系统(面向对象版)

    python实现学员管理系统(面向对象版)

    这篇文章主要为大家详细介绍了python面向对象版实现学员管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06
  • Python count()函数用法详解

    Python count()函数用法详解

    这篇文章主要给大家介绍了关于Python count()函数用法的相关资料,Python count()方法用于统计字符串里某个字符出现的次数,可选参数为在字符串搜索的开始与结束位置,需要的朋友可以参考下
    2024-02-02
  • python数据爬下来保存的位置

    python数据爬下来保存的位置

    在本篇文章里小编给大家整理的是关于python数据爬下来保存的位置,需要的朋友们可以参考下。
    2020-02-02
  • python实现购物车功能

    python实现购物车功能

    这篇文章主要为大家详细介绍了python实现购物车功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-02-02
  • python实现连续变量最优分箱详解--CART算法

    python实现连续变量最优分箱详解--CART算法

    今天小编就为大家分享一篇python实现连续变量最优分箱详解--CART算法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-11-11
  • Python opencv操作深入详解

    Python opencv操作深入详解

    这篇文章主要介绍了Python opencv操作深入详解,文中整理的比较详细,有感兴趣的同学可以学习下
    2021-03-03
  • python 公共方法汇总解析

    python 公共方法汇总解析

    这篇文章主要介绍了python 公共方法汇总解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-09-09

最新评论