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


Posted in Python onJanuary 17, 2021

实践环境

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源码分析详解的文章就介绍到这了,更多相关Python logging.getLogger源码分析内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
Python中多线程thread与threading的实现方法
Aug 18 Python
Python使用正则表达式实现文本替换的方法
Apr 18 Python
python中计算一个列表中连续相同的元素个数方法
Jun 29 Python
python 统计列表中不同元素的数量方法
Jun 29 Python
Python 3.x 判断 dict 是否包含某键值的实例讲解
Jul 06 Python
解决新django中的path不能使用正则表达式的问题
Dec 18 Python
python图形工具turtle绘制国际象棋棋盘
May 23 Python
Python matplotlib生成图片背景透明的示例代码
Aug 30 Python
Django 简单实现分页与搜索功能的示例代码
Nov 07 Python
Python Tornado之跨域请求与Options请求方式
Mar 28 Python
Python实现微信表情包炸群功能
Jan 28 Python
解决Python import .pyd 可能遇到路径的问题
Mar 04 Python
Python中的面向接口编程示例详解
Jan 17 #Python
Python学习之time模块的基本使用
Jan 17 #Python
python中re模块知识点总结
Jan 17 #Python
史上最详细的Python打包成exe文件教程
Jan 17 #Python
python制作微博图片爬取工具
Jan 16 #Python
python工具——Mimesis的简单使用教程
Jan 16 #Python
Python 内存管理机制全面分析
Jan 16 #Python
You might like
一个显示天气预报的程序
2006/10/09 PHP
如何修改和添加Apache的默认站点目录
2013/07/05 PHP
PHP中JSON的应用技巧
2015/10/10 PHP
php语言的7种基本的排序方法
2020/12/28 PHP
PHP实现的折半查找算法示例
2017/12/19 PHP
PHP利用缓存处理用户注册时的邮箱验证,成功后用户数据存入数据库操作示例
2019/12/31 PHP
Aster vs KG BO3 第二场2.19
2021/03/10 DOTA
jQuery 创建Dom元素
2010/05/07 Javascript
关于window.pageYOffset和document.documentElement.scrollTop
2011/04/05 Javascript
jQuery News Ticker 基于jQuery的即时新闻行情展示插件
2011/11/05 Javascript
Javascript实现DIV滚动自动滚动到底部的代码
2012/03/01 Javascript
引入JS文件IE6报语法错误或缺少对象问题的解决方法
2014/01/09 Javascript
Jquery在指定DIV加载HTML示例代码
2014/02/17 Javascript
JavaScript去除数组里重复值的方法
2015/07/13 Javascript
jQuery实现hover合成事件的方法
2015/08/06 Javascript
点击按钮出现60秒倒计时的简单js代码(推荐)
2016/06/07 Javascript
js addDqmForPP给标签内属性值加上双引号的函数
2016/12/24 Javascript
javascript 操作cookies详解及实例
2017/02/22 Javascript
three.js实现炫酷的全景3D重力感应
2018/12/30 Javascript
[03:38]TI4西雅图DOTA2前线报道 71专访
2014/07/08 DOTA
python 随机数生成的代码的详细分析
2011/05/15 Python
Scrapy框架CrawlSpiders的介绍以及使用详解
2017/11/29 Python
使用Python设计一个代码统计工具
2018/04/04 Python
Python离线安装PIL 模块的方法
2019/01/08 Python
python匿名函数lambda原理及实例解析
2020/02/07 Python
HTML5 Canvas标签使用收录
2009/07/07 HTML / CSS
HTML5教程之html 5 本地数据库(Web Sql Database)
2014/04/03 HTML / CSS
Spartoo比利时:欧洲时尚购物网站
2017/12/06 全球购物
英国内衣连锁店:Boux Avenue
2018/01/24 全球购物
全球立体声:World Wide Stereo
2018/09/29 全球购物
关爱留守儿童标语
2014/06/18 职场文书
文员岗位职责
2015/02/04 职场文书
房地产销售助理岗位职责
2015/04/14 职场文书
在 Python 中利用 Pool 进行多线程
2022/04/24 Python
Mysql开启外网访问
2022/05/15 MySQL
关于对TypeScript泛型参数的默认值理解
2022/07/15 Javascript