代码解析python标准库logging模块

作者:wangst4321 时间:2023-09-21 16:31:32 

问题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的方式。

来源:https://blog.csdn.net/wangst4321/article/details/8658859

标签:python,标准库,logging,模块
0
投稿

猜你喜欢

  • python散点图实例之随机漫步

    2021-01-19 21:05:58
  • 将Python的Django框架与认证系统整合的方法

    2022-05-09 20:33:15
  • 使用PHP开发Android应用程序技术介绍

    2023-07-02 09:40:53
  • C# 制作PictureBox圆形头像框并从数据库中读取头像

    2024-01-21 05:17:07
  • Python实用技巧之利用元组代替字典并为元组元素命名

    2022-08-21 19:39:42
  • Javascript与PHP验证用户输入URL地址是否正确

    2023-10-23 02:36:07
  • Golang TCP粘包拆包问题的解决方法

    2023-07-18 11:04:43
  • Python实现自动发消息自定义内容的操作代码

    2023-10-17 18:01:53
  • 一个修改Oracle数据库用户密码的小诀窍

    2009-09-30 15:29:00
  • 基于python实现的抓取腾讯视频所有电影的爬虫

    2023-02-20 22:01:39
  • python实现超简单端口转发的方法

    2022-12-25 04:00:53
  • 使用Python解析JSON数据的基本方法

    2021-07-03 13:54:21
  • python写入文件如何取消自动换行

    2023-11-28 22:39:11
  • Python算法之求n个节点不同二叉树个数

    2022-08-22 03:59:30
  • vue实现输入框的模糊查询的示例代码(节流函数的应用场景)

    2024-05-08 09:33:35
  • sql 语句 取数据库服务器上所有数据库的名字

    2024-01-24 15:58:06
  • 日式酒店电梯面板设计

    2008-06-08 13:23:00
  • Django auth 应用模块详解

    2023-05-20 11:17:17
  • Web标准学习:CSS样式书写风格

    2008-03-25 09:37:00
  • 深入解析PHP 5.3.x 的strtotime() 时区设定 警告信息修复

    2023-11-06 19:25:27
  • asp之家 网络编程 m.aspxhome.com