Python⽇志打印之Logger源码分析
⽇志打印之Logger源码分析
By:授客 QQ:1033553122 #实践环境
WIN 10
Python 3.6.5
#函数说明
getLogger函数位于logging/__init__.py脚本
#源码分析
_loggerClass = Logger
# ...略
root = RootLogger(WARNING)
< = root
Logger.manager = )
# ...略
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(name)
else:
return root
结论:如函数注释所述,如果调⽤getLogger时,如果没有指定函数参数(即要获取的⽇志打印器名称)或者参数值不为真,则默认返回root打印器
##Logger(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.
"""
< = rootnode
self.disable = 0
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 "u" 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
# ... 略
结论:如果调⽤Logger()时,有指定⽇志打印器名称,且名称为真(不为空字符串,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仅是占位⽤,不是真的打印器,这⾥为了⽅便描述,暂且称之为PlaceHo            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 = # 则把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⽇志打印器的上级 
结论:⽇志打印器都是分⽗⼦级的,这个⽗⼦层级是怎么形成的,参见上述函数代码注解

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。