Python 日志打印之logging.getLogger源码分析
日志打印之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源码分析的更多相关文章
- python 日志打印之logging使用介绍
python 日志打印之logging使用介绍 by:授客QQ:1033553122 测试环境: Python版本:Python 2.7 简单的将日志打印到屏幕 import logging lo ...
- Python 日志打印之logging.config.dictConfig使用总结
日志打印之logging.config.dictConfig使用总结 By:授客 QQ:1033553122 #实践环境 WIN 10 Python 3.6.5 #函数说明 logging.confi ...
- Python学习---Django的request.post源码分析
request.post源码分析: 可以看到传递json后会帮我们dumps处理一次最后一字节形式传递过去
- python成长之路10——socketserver源码分析
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM,0) 参数一:地址簇 socket.AF_INET ipv4(默认) socket.AF_INE ...
- Python创建容器和集合之源码分析
_collections_abc.py文件中提供了许多抽象基类,这些类将集合分解成许多互相独立的属性集 __all__ = ["Awaitable", "Coroutin ...
- 源码分析 RocketMQ DLedger(多副本) 之日志复制(传播)
目录 1.DLedgerEntryPusher 1.1 核心类图 1.2 构造方法 1.3 startup 2.EntryDispatcher 详解 2.1 核心类图 2.2 Push 请求类型 2. ...
- [Abp vNext 源码分析] - 文章目录
一.简要介绍 ABP vNext 是 ABP 框架作者所发起的新项目,截止目前 (2019 年 2 月 18 日) 已经拥有 1400 多个 Star,最新版本号为 v 0.16.0 ,但还属于预览版 ...
- 日志那点事儿——slf4j源码剖析
前言: 说到日志,大多人都没空去研究,顶多知道用logger.info或者warn打打消息.那么commons-logging,slf4j,logback,log4j,logging又是什么关系呢?其 ...
- 精尽Spring Boot源码分析 - 日志系统
该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...
随机推荐
- 新挖个坑,准备学习一下databricks的spark博客
挖坑 https://databricks.com/blog 一.spark3.0特性(Introducing Apache Spark 3.0) 1.通过通过自适应查询执行,动态分区修剪和其他优化使 ...
- Css:常用的去除默认样式
*{ padding: 0; margin: 0; } ul,ol{ list-style: none; } a,a:hover,a:link,a:visited,a:active{ ...
- Android原子操作——android_atomic_cmpxchg
网络给我们带来了很多方便,查阅我们目前认知范围外的道理.但是,凡事也要学会分辨,不然可能会误导你. 话说,最近的一个项目(Mercury-Project),接近尾声中.然而,在调试一个demo时,却遇 ...
- Sql Server 数据把列根据指定的内容拆分数据
今天由于工作需要,需要把数据把列根据指定的内容拆分数据 其中一条数据实例 select id , XXXX FROM BIZ_PAPER where id ='4af210ec675927fa016 ...
- Intellij idea 创建java web
https://blog.csdn.net/qq_41592771/article/details/80577791 将tomcat服务器lib目录下面的servlet-api.jar复制到lib文件 ...
- js上 五、运算符-1
5.1.认识运算符 什么是运算符? 运算符用于执行程序代码运算,会针对一个以上操作数项目来进行运算. 运算符的应用: 购物车:计算总价,数量: **Js ** 中有哪些运算符? 算术运算符.赋值运算符 ...
- gcc编译阶段打印宏定义的内容
背景 总所周知,代码量稍微大一点的C/C++项目的一些宏定义都会比较复杂,有时候会嵌套多个#if/#else判断分支和一堆#ifdef/#undef让你单看代码的话很难判断出宏定义的具体内容. 如果有 ...
- 流程控制之☞ while 和 for 的故事
学习三连鞭... 什么是循环? 为什么要有循环? 如何用循环? 循环的基本语法:while 和 for 先来看while循环: while条件:首先得是个循环体. 1.如果条件为真,那么循 ...
- day019python之面向对象基础1
面向对象基础 目录 面向对象基础 1 面向对象基础 1.1 面向对象的由来 1.2 面向对象编程介绍 1.2.1 回顾面向过程设计 1.2.2 面向对象设计 2 类与对象 2.1 基本使用 2.2 示 ...
- C# 中 ConcurrentDictionary 一定线程安全吗?
根据 .NET 官方文档的定义:ConcurrentDictionary<TKey,TValue> Class 表示可由多个线程同时访问的线程安全的键/值对集合.这也是我们在并发任务中比较 ...