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):
  """
  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源码分析内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 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

  • SpringCloud微服务续约实现源码分析详解

    目录 一.前言 二.客户端续约 1.入口 构造初始化 initScheduledTasks()调度执行心跳任务 2.TimedSupervisorTask组件 构造初始化 TimedSupervisorTask#run()任务逻辑 3.心跳任务 HeartbeatThread私有内部类 发送心跳 4.发送心跳到注册中心 构建请求数据发送心跳 三.服务端处理客户端续约 1.InstanceRegistry#renew()逻辑 2.PeerAwareInstanceRegistryImpl#rene

  • React commit源码分析详解

    目录 总览 commitBeforeMutationEffects commitMutationEffects 插入 dom 节点 获取父节点及插入位置 判断当前节点是否为单节点 在对应位置插入节点 更新 dom 节点 更新 HostComponent 更新 HostText 删除 dom 节点 unmountHostComponents commitNestedUnmounts commitUnmount commitLayoutEffects 执行生命周期 处理回调 总结 总览 commit

  • python django事务transaction源码分析详解

    python Django事务 网上关于django1.6的事务资料很多,但是1.8的却搜不到任何资料,自己要用的时候费了不少劲就是不行,现在记下要用的人少走弯路 version:Django 1.8 事务官方文档 事务中文文档里面介绍很多方法,不一一赘述,按照文档即可,下面只分析下atomic方法的源码 按照官方文档 transaction.atomic 有两种用法装饰器和上下文管理器 # atomic() 方法 # from django.db import transaction ####

  • Android Handler,Message,MessageQueue,Loper源码解析详解

    本文主要是对Handler和消息循环的实现原理进行源码分析,如果不熟悉Handler可以参见博文< Android中Handler的使用>,里面对Android为何以引入Handler机制以及如何使用Handler做了讲解. 概括来说,Handler是Android中引入的一种让开发者参与处理线程中消息循环的机制.我们在使用Handler的时候与Message打交道最多,Message是Hanlder机制向开发人员暴露出来的相关类,可以通过Message类完成大部分操作Handler的功能.但

  • Java Spring @Lazy延迟注入源码案例详解

    前言 有时候我们会在属性注入的时候添加@Lazy注解实现延迟注入,今天咱们通过阅读源码来分析下原因 一.一个简单的小例子 代码如下: @Service public class NormalService1 { @Autowired @Lazy private MyService myService; public void doSomething() { myService.getName(); } } 作用是为了进行延迟加载,在NormalService1进行属性注入的时候,如果MyServ

  • kotlin源码结构层次详解

    目录 协程源码的结构 基础层 中间层 平台层 协程源码的结构 在研究Kotlin源码之前,得先搞懂Kotlin源码结构分布.不然找不到该看哪里的代码.看源码之前当然先得有一个目标,最好是带着这个目标去看源码才比较有针对性,抓主流程,不然可能会陷入浩瀚的源码细节中. 协程源码,按道理可以分成2个仓库,一个是Kotlin仓库,一个是Kotlin协程仓库. Kotlin仓库 https://github.com/JetBrains/kotlin 协程仓库 kotlinx.coroutines http

  • Node.js高级编程cluster环境及源码调试详解

    目录 前言 准备调试环境 编译 Node.js 准备 IDE 环境 Cluster 源码调试 SharedHandle RoundRobinHandle 为什么端口不冲突 SO_REUSEADDR 补充 SharedHandle 和 RoundRobinHandle 两种模式的对比 前言 日常工作中,对 Node.js 的使用都比较粗浅,趁未羊之际,来学点稍微高级的,那就先从 cluster 开始吧. 尼古拉斯张三说过,“带着问题去学习是一个比较好的方法”,所以我们也来试一试. 当初使用 clu

  • java TreeMap源码解析详解

    java TreeMap源码解析详解 在介绍TreeMap之前,我们来了解一种数据结构:排序二叉树.相信学过数据结构的同学知道,这种结构的数据存储形式在查找的时候效率非常高. 如图所示,这种数据结构是以二叉树为基础的,所有的左孩子的value值都是小于根结点的value值的,所有右孩子的value值都是大于根结点的.这样做的好处在于:如果需要按照键值查找数据元素,只要比较当前结点的value值即可(小于当前结点value值的,往左走,否则往右走),这种方式,每次可以减少一半的操作,所以效率比较高

  • IOS身份证识别(OCR源码)详解及实例代码

    IOS身份证识别(OCR源码)详解 最近项目用到身份证识别,在github上搜了一堆demo,在Google上找了一堆代码,有能识别出证件照的,但是都是打包成.a的静态库,没有源码,我努力吃了几天书,有了一点研究成果,现在贴出来与大家分享,要是有更好的方法,希望大神指正,共同探讨解决方案.(以下代码本人亲测可用,正在进一步探索智能识别,如有兴趣,请加入) 这里用到了两个开源库:OpenCV.TesseractOCRiOS,两个语言包chi_sim.eng.身份证识别的流程主要有:灰度化,阀值二值

随机推荐