日志打印之logging.getLogger源码分析

By:授客 QQ:1033553122

#实践环境

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):
    """
    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 





结论:如函数注释所述,如果调用getLogger时,如果没有指定函数参数(即要获取的日志打印器名称)或者参数值不为真,则默认返回root打印器



##Logger.manager.getLogger(self, name)源码分析


该函数位于logging/__init__.py脚本



class Manager(object):
    """
    There is [under normal circumstances] just one Manager instance, which
    holds the hierarchy of loggers.
    """
    def __init__(self, rootnode):
        """
        Initialize the manager with the root node of the logger hierarchy.
        """
        self.root = rootnode
        self.disable = 0
        self.emittedNoHandlerWarning = False
        self.loggerDict = {}
        self.loggerClass = None
        self.logRecordFactory = None

    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
        if not isinstance(name, str):
            raise TypeError('A logger name must be a string')
        _acquireLock()
        try:  if name in self.loggerDict:
                rv = self.loggerDict[name]
                if isinstance(rv, PlaceHolder):
                    ph = rv
                    rv = (self.loggerClass or _loggerClass)(name)
                    rv.manager = self
                    self.loggerDict[name] = rv
                    self._fixupChildren(ph, rv)
                    self._fixupParents(rv)
            else:
                rv = (self.loggerClass or _loggerClass)(name) # _loggerClass = Logger
                rv.manager = self
                self.loggerDict[name] = rv
                self._fixupParents(rv) finally:
            _releaseLock()
        return rv



###Logger源码分析


_nameToLevel = {
    'CRITICAL': CRITICAL,
    'FATAL': FATAL,
    'ERROR': ERROR,
    'WARN': WARNING,
    'WARNING': WARNING,
    'INFO': INFO,
    'DEBUG': DEBUG,
    'NOTSET': NOTSET,
}

# ...略

def _checkLevel(level):
    if isinstance(level, int):
        rv = level
    elif str(level) == level:
        if level not in _nameToLevel:
            raise ValueError("Unknown level: %r" % level)
        rv = _nameToLevel[level]
    else:
        raise TypeError("Level not an integer or a valid string: %r" % level)
    return rv

# ...略
class PlaceHolder(object):
    """
    PlaceHolder instances are used in the Manager logger hierarchy to take
    the place of nodes for which no loggers have been defined. This class is
    intended for internal use only and not as part of the public API.
    """
    def __init__(self, alogger):
        """
        Initialize with the specified logger being a child of this placeholder.
        """
        self.loggerMap = { alogger : None }

    def append(self, alogger):
        """
        Add the specified logger as a child of this placeholder.
        """
        if alogger not in self.loggerMap:
            self.loggerMap[alogger] = None



class Logger(Filterer):
    """
    Instances of the Logger class represent a single logging channel. A
    "logging channel" indicates an area of an application. Exactly how an
    "area" is defined is up to the application developer. Since an
    application can have any number of areas, logging channels are identified
    by a unique string. Application areas can be nested (e.g. an area
    of "input processing" might include sub-areas "read CSV files", "read
    XLS files" and "read Gnumeric files"). To cater for this natural nesting,
    channel names are organized into a namespace hierarchy where levels are
    separated by periods, much like the Java or Python package namespace. So
    in the instance given above, channel names might be "input" for the upper
    level, and "input.csv", "input.xls" and "input.gnu" for the sub-levels.
    There is no arbitrary limit to the depth of nesting.
    """
    def __init__(self, name, level=NOTSET):
        """
        Initialize the logger with a name and an optional level.
        """  Filterer.__init__(self)
        self.name = name
        self.level = _checkLevel(level)
        self.parent = None
        self.propagate = True
        self.handlers = []
        self.disabled = False # ... 略




结论:如果调用logging.getLogger()时,有指定日志打印器名称,且名称为真(不为空字符串,0,False等False值),

1)如果名称为不存在的日志打印器名称,则,且参数值为真,但是即要获取的日志打印器名称)或者参数值不为真,则创建一个名为给定参数值的日志打印器,该日志打印器,默认级别默认为NOTSET,disable_existing_loggers配置为False,propagate配置为True。然后在日志打印器字典中记录该名称和日志打印器的映射关系,接着调用 _fixupParents(创建的日志打印器实例)类实例方法--为日志打印器设置上级日志打印器,最后返回该日志打印器。

2)如果名称已存在日志打印器名称,则获取该日志打印器,然后判断日志打印器是否为PlaceHolder类实例,如果是,则创建一个名为所给参数值的日志打印器,同第1)点,该日志打印器,默认级别默认为NOTSET,disable_existing_loggers配置为False,propagate配置为True。然后在日志打印器字典中记录该名称和日志打印器的映射关系,接着调用 _fixupParents(创建的打印器实例)类实例方法,_fixupChildren(PlaceHolder类实例--根据名称获取的日志打印器,新建的日志打印器实例)--为新建日志打印器设置上级日志打印器,PlaceHolder类实例现有下级PlaceHolder日志打印器实例重新设置上级日志打印器,最后返回该日志打印器。



###_fixupParents及_fixupChildren函数源码分析

# _fixupParents

# ...略
class Manager(object):
    # ...略
    def _fixupParents(self, alogger):
        """
        Ensure that there are either loggers or placeholders all the way
        from the specified logger to the root of the logger hierarchy.
        """  name = alogger.name # 获取日志打印器名称
        i = name.rfind(".") 
        rv = None # 存放alogger日志打印器的上级日志打印器
        while (i > 0) and not rv: # 如果名称中存在英文的点,并且找到上级日志打印器
            substr = name[:i] # 获取名称中位于最后一个英文的点的左侧字符串(暂且称至为 点分上级)
            if substr not in self.loggerDict: # 如果 点分上级 不存在日志打印器字典中
                self.loggerDict[substr] = PlaceHolder(alogger) # 创建PlaceHolder实例作为 点分上级 对应的日志打印器 # 继续查找点分上级日志打印器 # 注意,这里的PlaceHolder仅是占位用,不是真的打印器,这里为了方便描述,暂且称之为PlaceHolder日志打印器
            else: # 否则
                obj = self.loggerDict[substr] # 获取 点分上级 对应的日志打印器
                if isinstance(obj, Logger): # 如果为Logger实例,如果是,则跳出循环,执行 # 为日志打印器设置上级
                    rv = obj
                else: # 否则
                    assert isinstance(obj, PlaceHolder) # 断言它为PlaceHolder的实例
                    obj.append(alogger) # 把日志打印器添加为点分上级对应的PlaceHolder日志打印器实例的下级日志打印器 执行 # 继续查找点分上级日志打印器
            i = name.rfind(".", 0, i - 1) # 继续查找点分上级日志打印器
        if not rv: # 找不到点分上级、或者遍历完所有点分上级,都没找到上级日志打印器
            rv = self.root # 则 把root日志打印器设置为alogger日志打印器的上级日志打印器
        alogger.parent = rv # 为日志打印器设置上级 def _fixupChildren(self, ph, alogger):
        """
        Ensure that children of the placeholder ph are connected to the
        specified logger.
        """ name = alogger.name  # 获取日志打印器名称
        namelen = len(name)  # 获取日志打印器名称长度 
        for c in ph.loggerMap.keys(): # 遍历获取的PlaceHolder日志打印器实例的子级日志打印器
            #The if means ... if not c.parent.name.startswith(nm)
            if c.parent.name[:namelen] != name: # 如果PlaceHolder日志打印器实例名称不以alogger日志打印器名称为前缀,
                alogger.parent = c.parent # 那么,设置alogger日志打印器的上级日志打印器为PlaceHolder日志打印器
                c.parent = alogger # 设置alogger日志打印器为PlaceHolder日志打印器原有下级PlaceHolder日志打印器的上级 



结论:日志打印器都是分父子级的,这个父子层级是怎么形成的,参见上述函数代码注解




Python 日志打印之logging.getLogger源码分析的更多相关文章

  1. Python学习(3) (变量的基本使用、定义、类型、计算、类型转换、输入输出、命名)

    Python学习(3)一、python变量的定义二、pycharm单步执行查看变量值三、python变量的类型四、python变量间的计算五、python变量的输入输出1. input 函数用法2. 类型转换函数3. 变量的格式化输出六、python变量的命名1.标识符2.关键字3.变量的命名规则程......

  2. python中pyqtgraph知识点总结

    PyQtGraph是纯Python的,只是它底层调用的是PyQt,也就是Qt的Python封装,底层用C/C++语言开发的库,它是在PyQt的基础上开发的可视化控件,相较于老牌的绘制库,PyQtGraph在绘图速度上特别突出,保证绘图的美观性以及强大功能前提下,能够极高的效率去绘制图形,下面一起来详......

  3. 使用Python+Appuim 清理微信的方法

    使用 Appium安装一下 Python 用到的模块pip install Appium-Python-Client获取好友列表在 Pycharm 中配置一下启动环境desired_capabilities = {'platformName': 'Android', # 操作系统'deviceNam......

  4. python实现自幂数的示例代码

    1、什么是自幂数?前文介绍过 python 实现水仙花数,其实水仙花数为自幂数的一种,即,3位自幂数。自幂数是指一个 n 位数,它的每个位上的数字的 n 次幂之和等于它本身。(例如:当n为3时,有1^3 + 5^3 + 3^3 = 153,153即是n为3时的一个自幂数)自幂数-百度百科2、自幂......

  5. 五分钟学会怎么用Pygame做一个简单的贪吃蛇

    Pygame 是一组用来开发游戏软件的 Python 程序模块,基于 SDL 库的基础上开发。我们今天将利用它来制作一款大家基本都玩过的小游戏——贪吃蛇。一、需要导入的包?123import pygameimport timeimport randompygame:获取图形组件构建游戏time:主要......

  6. Python优化机制:常量折叠

    英文:https://arpitbhayani.me/blogs/constant-folding-python作者:arprit译者:豌豆花下猫(“Python猫”公众号作者)声明:本翻译是出于交流学习的目的,基于 CC BY-NC-SA 4.0 授权协议。为便于阅读,内容略有改动。每种编程语言为......

  7. Python字符串对齐、删除字符串不需要的内容以及格式化打印字符

    删除字符串中不需要的内容1、strip()方法strip:默认是去掉首尾的空白字符,但是也可以指定其他字符;lstrip:只去掉左边的;rstrip:只去掉右边的;print('+++apple '.strip()) # '+++apple'print('+++apple '.lstrip('+')......

  8. 如何用python批量调整视频声音

    今天来研究python中moviepy模块的用途近来有大量处理视频的需求,常会碰到一个问题是下载的视频音量过小,会需要将它调大声,虽然有在线工具VideoLouder可以免费调整视频音量大小,但毕竟若量很大的话一个一个上传视频也是挺麻烦的事情,因此决定再用程序帮忙解决。使用教学基础程序调整一个视频音......

  9. 详解Python之Scrapy爬虫教程NBA球员数据存放到Mysql数据库

    获取要爬取的URL爬虫前期工作用Pycharm打开项目开始写爬虫文件字段文件items# Define here the models for your scraped items## See documentation in:# https://docs.scrapy.org/en/latest/......

  10. 【python接口自动化】- 正则用例参数化

    我们在做接口自动化的时候,处理接口依赖的相关数据时,通常会使用正则表达式来进行提取相关的数据。正则表达式,又称正规表示式、正规表示法、正规表达式、规则表达式、常规表示法(Regular Expression,在代码中常简写为regex、regexp或RE) 。它是一个特殊的字符序列,它能帮助你方便的......

随机推荐

  1. Perl字符串处理函数大全

    下面请看本文详细介绍.Perl字符串处理函数1. index语法:position=index(string,substring,position);语义:返回子串substring在字符串string中的位置,如果不存在则返回-1。参数position是可选项,表示匹配之前跳过的字符数,或者说从该......

  2. MySQL 5.6.35 索引优化导致的死锁案例解析

    本文描述了在一次压测过程中,由于Index Merge优化导致的死锁,详细描述了死锁产生的原因以及解决方案,并顺便介绍了Mysql索引结构及加锁机制。通过本文,大家可以掌握死锁分析的基本理论和一般方法,为工作中快速解决实际出现的死锁提供思路。一、背景随着公司业务的发展,商品库存从商品中心独立出来成为......

  3. Opencv+Python识别PCB板图片的步骤

    任务要求:基于模板匹配算法识别PCB板型号使用工具:Python3、OpenCV使用模板匹配算法,模板匹配是一种最原始、最基本的模式识别方法,研究某一特定对象物的图案位于图像的什么地方,进而识别对象物,模板匹配具有自身的局限性,主要表现在它只能进行平行移动,即原图像中的匹配目标不能发生旋转或大小变化......

  4. 利用python为PostgreSQL的表自动添加分区

    PostgreSQL引进“分区”表特性,解放了之前采用“表继承”+“触发器”来实现分区表的繁琐、低效。而添加分区,都是手动执行SQL。 演示目的:利用python来为PostgreSQL的表自动添加分区。python版本:python3+ pip3 install psycopg2 一......

  5. Python学习(1) (python特点、优缺点)

    Python学习(1)一、python的特点二、python的优缺点1.优点2.缺点三、python源程序的基本概念一、python的特点1. python 是完全面向对象的语言 函数、模块、数字、字符串都是对象,在python中一切皆为对象完全支持继承、重载、多重继承支持重载运算符,也支持泛型设计......

  6. 文件监控性能问题【BUG】

    文件监控性能问题【BUG】背景:JAVA写了一个文件夹目录监控的程序,使用的是org.apache.commons.io.monitor 包,项目稳定运行了一个月,现场反馈,文件夹数据处理越来越慢,等到数据推送到前端要好几分钟,于是开始了寻找问题的路程。监控代码之前写的文件监控代码问题发现我在Ap......

  7. Vue中引入svg图标的两种方式

    Vue中引入svg图标的方式Vue中引入svg图标的方式一安装yarn add svg-sprite-loader --devsvg组件index.vueexport default {name: 'SvgIcon',props: {// svg 的名称svgName: {type: String,......

  8. 巧用 Lazy 解决.NET Core中的循环依赖问题

    原文作者: Thomas Levesque原文链接:https://thomaslevesque.com/2020/03/18/lazily-resolving-services-to-fix-circular-dependencies-in-net-core/循环依赖的问题在构建应用程序时,良好的......

  9. Python数据可视化分析--豆瓣电影Top250

    Python数据分析–豆瓣电影Top250利用Python爬取豆瓣电影TOP250并进行数据分析,对于众多爬虫爱好者,应该并不陌生。很多人都会以此作为第一个练手的小项目。当然这也多亏了豆瓣的包容,没有加以太多的反爬措施,对新手比较友好。手动声明版权声明:本文为博主原创文章,创作不易本文链接:http......

  10. Python进阶丨如何创建你的第一个Python元类?

    摘要:通过本文,将深入讨论Python元类,其属性,如何以及何时在Python中使用元类。Python元类设置类的行为和规则。元类有助于修改类的实例,并且相当复杂,是Python编程的高级功能之一。通过本文,将深入讨论Python元类,其属性,如何以及何时在Python中使用元类。本文介绍以下概念:......