代码解析python标准库logging模块

目录
  • 问题1:如何获取caller的(文件名,行号,函数名)?
    • findCaller内容如下:
    • currentframe函数的定义:
  • 问题2: Logger对象的层级,父子关系如何实现的?
    • Manager的getLogger()定义如下:

问题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模块的资料请关注我们其它相关文章!

(0)

相关推荐

  • python (logging) 日志按日期、大小回滚的操作

    描述: 日志按日期.大小回滚 代码: # -*- coding: utf-8 -*- import os import logging.handlers log_dir = os.path.dirname(os.path.abspath(__file__)) + os.sep + 'logs' if not os.path.isdir(log_dir): os.makedirs(log_dir) # CONSTANT VARIABLES MODULE_NAME = 'my_module' LOG

  • Python日志打印里logging.getLogger源码分析详解

    实践环境 WIN 10 Python 3.6.5 函数说明 logging.getLogger(name=None) getLogger函数位于logging/__init__.py脚本 源码分析 _loggerClass = Logger # ...略 root = RootLogger(WARNING) Logger.root = root Logger.manager = Manager(Logger.root) # ...略 def getLogger(name=None): "&quo

  • 解决python logging遇到的坑 日志重复打印问题

    python 中 logging模块 假如遇到 多线程 或者 多进程 或者在web框架中自定义logging的话(一个请求就是一个独立的线程)非常容易重复打印日志 和造成内存崩溃,所以: 解决方法如下: 重写日志方法 用类: class Log(): import logging def __init__(self): self.logger = logging.getLogger(__name__) # 以下三行为清空上次文件 # 这为清空当前文件的logging 因为logging会包含所有

  • Python中的logging模块实现日志打印

    目录 方法1:配置并输出日志到标准输出 方法2:配置输出到日志文件 前言: 大家在写代码的时候,经常会使用print打印日志方便排查问题,然而print的问题就是太过简单,缺少时间.日志级别等格式化信息. Python自带的logging模块,很简单就能实现日志的配置和打印,它有两种用法,方法1是把日志输出到标准输出,其实就是命令行界面,方法2则是输出日志到文件,更加方便. 方法1:配置并输出日志到标准输出 import logging   LOG_FORMAT = "%(asctime)s -

  • python 实现logging动态变更输出日志文件名

    python作为一门非常容易上手的脚本语言,日志输出更是简单,logging模块,简单的设置配置和属性,就能实现到控制台输出日志,在basicConfig()设置文件名,就能够将日志信息写入文件,简直是简单到不能再简单. 最近在项目中就遇到一个日志问题,使用python编写的服务程序一直运行,连续处理一些任务,每个任务的关键信息都需要输出到文件中,便于维护人员查看,可是对于简单实用logging来说,日志写入文件非常简单,由于服务程序连续运行,一直向一个文件记录日志信息有些不妥,有常识的开发人员

  • 代码解析python标准库logging模块

    目录 问题1:如何获取caller的(文件名,行号,函数名)? findCaller内容如下: currentframe函数的定义: 问题2: Logger对象的层级,父子关系如何实现的? Manager的getLogger()定义如下: 问题1:如何获取caller的(文件名,行号,函数名)? 当新增一条log记录时,最终将调用Logger类的_log方法,这个方法首先会创建一个LogRecord对象.LogRecord对象需要(filename, lineno, funcname)参数信息.

  • python标准库压缩包模块zipfile和tarfile详解(常用标准库)

    目录 常用的标准库 zip格式 ZipFile参数说明 操作含义 压缩方法 常用方法 tar包 和 gz.bz2.xz格式 删除压缩包中的文件 常用的标准库 在我们常用的系统windows和Linux系统中有很多支持的压缩包格式,包括但不限于以下种类:rar.zip.tar,以下的标准库的作用就是用于压缩解压缩其中一些格式的压缩包. zip格式 import zipfile zipfile模块操作压缩包使用ZipFile类进行操作,使用方法和open的使用方法很相似,也是使用r.w.x.a四种操

  • python标准库OS模块详解

    python标准库OS模块简介 os就是"operating system"的缩写,顾名思义,os模块提供的就是各种 Python 程序与操作系统进行交互的接口.通过使用os模块,一方面可以方便地与操作系统进行交互,另一方面页可以极大增强代码的可移植性.如果该模块中相关功能出错,会抛出OSError异常或其子类异常. 注意 如果是读写文件的话,建议使用内置函数open(): 如果是路径相关的操作,建议使用os的子模块os.path: 如果要逐行读取多个文件,建议使用fileinput模

  • python标准库OS模块函数列表与实例全解

    Python OS模块库详解 os就是"operating system"的缩写,顾名思义,os模块提供的就是各种 Python 程序与操作系统进行交互的接口.通过使用os模块,一方面可以方便地与操作系统进行交互,另一方面页可以极大增强代码的可移植性.如果该模块中相关功能出错,会抛出OSError异常或其子类异常. 注意 如果是读写文件的话,建议使用内置函数open(): 如果是路径相关的操作,建议使用os的子模块os.path: 如果要逐行读取多个文件,建议使用fileinput模块

  • Python标准库uuid模块(生成唯一标识)详解

    目录 UUID格式组成 Python 中的uuid 模块 uuid1() 中的getnode() UUID实际应用 UUID 的劣势 参考资料: 总结 UUID (Universally Unique Identifier,通用唯一标识)是一个128位的用于计算机系统中以识别信息的数目,虽然生成UUID的概率不为零,但是无限接近零,因此可以忽略不记,如此一来,每个人都可以建立不与其他人冲突的UUID. UUID格式组成 规范的文本中,UUID的十六个八位字节标识位32个十六进制(基数16)数字,

  • python标准库random模块处理随机数

    目录 前言 1. 常用函数 2. 不常用函数 3. 使用示例 3.1 生成随机密码 前言 random模块实现了各种分布的伪随机数生成器. 伪随机数:人类使用算法等方式,以一个基准(也被称为种子,常用的是时间戳)来构造一系列数字,这些数字的特性符合人们所理解的随机数.一旦算法和种子都确定,产生的随机数序列也是确定的,所以称为伪随机数. 1. 常用函数 常用函数 说明 random.seed(a) 设置初始化随机种子,可输出相同随机数序列:a取整数或浮点数,不设置时默认以系统时间为种子 rando

  • Python标准库defaultdict模块使用示例

    Python标准库中collections对集合类型的数据结构进行了很多拓展操作,这些操作在我们使用集合的时候会带来很多的便利,多看看很有好处. defaultdict是其中一个方法,就是给字典value元素添加默认类型,之前看到过但是没注意怎么使用,今天特地瞅了瞅. 首先是各大文章介绍的第一个例子: 复制代码 代码如下: import collections as coll    def default_factory():      return 'default value'    d =

  • 一篇文章带你了解python标准库--random模块

    目录 1. random库基本介绍 2. random库概述 2.1 基本随机函数 2.2 扩展随机函数 3. 随机数函数的使用 4. 实例 总结 1. random库基本介绍 Random库时使用随机数的python标准库 伪随机数:采用梅森旋转算法生成的(伪)随机序列中的元素 Random库主要用于生成随机数 使用random库:import random 2. random库概述 Random库包含两类函数,常用共8个 基本随机函数:seed() random() 扩展随机函数:randi

  • 一篇文章带你了解python标准库--os模块

    目录 1. os库基本介绍 2. os库之路径操作 3. os库之进程管理 4. os库之环境参数 5. 案例 总结 目前,计算机上主流的操作系统有Windows.Unix.Mac OS等,os模块为多操作系统的访问提供了相关功能的支持,涉及对文件相关操作功能的实现,系统访问path路径的操作,shell命令行操作,Linux扩展属性的操作,流程管理,CPU等硬件相关信息的获取,基于操作系统的真正的随机数的操作及相关的一些系统常量进行介绍. 1. os库基本介绍 Os库提供通用的.基本的操作系统

  • 一篇文章带你了解python标准库--time模块

    目录 1. 调用语法: 2. time概述 3. 时间获取 4. 时间格式化(将时间以合理的方式展示出来) 5. 程序计时应用 6. 示例 总结 Time库是python中处理时间的标准库 1. 调用语法: import time time.<b>() 计算机时间的表达,提供获取系统时间并格式化输出功能 提供提供系统精确即使功能,用于程序性能分析 2. time概述 time库包括三类函数 时间获取: time() ctime() gmtime() 时间格式化: strftime() strp

随机推荐