Python日志打印里logging.getLogger源码分析详解
实践环境
WIN10
Python3.6.5
函数说明
logging.getLogger(name=None)
getLogger函数位于logging/__init__.py脚本
源码分析
_loggerClass=Logger #...略 root=RootLogger(WARNING) Logger.root=root Logger.manager=Manager(Logger.root) #...略 defgetLogger(name=None): """ Returnaloggerwiththespecifiedname,creatingitifnecessary. Ifnonameisspecified,returntherootlogger. """ ifname: returnLogger.manager.getLogger(name) else: returnroot
结论:如函数注释所述,如果调用getLogger时,如果没有指定函数参数(即要获取的日志打印器名称)或者参数值不为真,则默认返回root打印器
Logger.manager.getLogger(self,name)源码分析
该函数位于logging/__init__.py脚本
classManager(object):
"""
Thereis[undernormalcircumstances]justoneManagerinstance,which
holdsthehierarchyofloggers.
"""
def__init__(self,rootnode):
"""
Initializethemanagerwiththerootnodeoftheloggerhierarchy.
"""
self.root=rootnode
self.disable=0
self.emittedNoHandlerWarning=False
self.loggerDict={}
self.loggerClass=None
self.logRecordFactory=None
defgetLogger(self,name):
"""
Getaloggerwiththespecifiedname(channelname),creatingit
ifitdoesn'tyetexist.Thisnameisadot-separatedhierarchical
name,suchas"a","a.b","a.b.c"orsimilar.
IfaPlaceHolderexistedforthespecifiedname[i.e.thelogger
didn'texistbutachildofitdid],replaceitwiththecreated
loggerandfixuptheparent/childreferenceswhichpointedtothe
placeholdertonowpointtothelogger.
"""
rv=None
ifnotisinstance(name,str):
raiseTypeError('Aloggernamemustbeastring')
_acquireLock()
try:
ifnameinself.loggerDict:
rv=self.loggerDict[name]
ifisinstance(rv,PlaceHolder):
ph=rv
rv=(self.loggerClassor_loggerClass)(name)
rv.manager=self
self.loggerDict[name]=rv
self._fixupChildren(ph,rv)
self._fixupParents(rv)
else:
rv=(self.loggerClassor_loggerClass)(name)#_loggerClass=Logger
rv.manager=self
self.loggerDict[name]=rv
self._fixupParents(rv)
finally:
_releaseLock()
returnrv
Logger源码分析
_nameToLevel={
'CRITICAL':CRITICAL,
'FATAL':FATAL,
'ERROR':ERROR,
'WARN':WARNING,
'WARNING':WARNING,
'INFO':INFO,
'DEBUG':DEBUG,
'NOTSET':NOTSET,
}
#...略
def_checkLevel(level):
ifisinstance(level,int):
rv=level
elifstr(level)==level:
iflevelnotin_nameToLevel:
raiseValueError("Unknownlevel:%r"%level)
rv=_nameToLevel[level]
else:
raiseTypeError("Levelnotanintegeroravalidstring:%r"%level)
returnrv
#...略
classPlaceHolder(object):
"""
PlaceHolderinstancesareusedintheManagerloggerhierarchytotake
theplaceofnodesforwhichnologgershavebeendefined.Thisclassis
intendedforinternaluseonlyandnotaspartofthepublicAPI.
"""
def__init__(self,alogger):
"""
Initializewiththespecifiedloggerbeingachildofthisplaceholder.
"""
self.loggerMap={alogger:None}
defappend(self,alogger):
"""
Addthespecifiedloggerasachildofthisplaceholder.
"""
ifaloggernotinself.loggerMap:
self.loggerMap[alogger]=None
classLogger(Filterer):
"""
InstancesoftheLoggerclassrepresentasingleloggingchannel.A
"loggingchannel"indicatesanareaofanapplication.Exactlyhowan
"area"isdefinedisuptotheapplicationdeveloper.Sincean
applicationcanhaveanynumberofareas,loggingchannelsareidentified
byauniquestring.Applicationareascanbenested(e.g.anarea
of"inputprocessing"mightincludesub-areas"readCSVfiles","read
XLSfiles"and"readGnumericfiles").Tocaterforthisnaturalnesting,
channelnamesareorganizedintoanamespacehierarchywherelevelsare
separatedbyperiods,muchliketheJavaorPythonpackagenamespace.So
intheinstancegivenabove,channelnamesmightbe"input"fortheupper
level,and"input.csv","input.xls"and"input.gnu"forthesub-levels.
Thereisnoarbitrarylimittothedepthofnesting.
"""
def__init__(self,name,level=NOTSET):
"""
Initializetheloggerwithanameandanoptionallevel.
"""
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
#...略
classManager(object):
#...略
def_fixupParents(self,alogger):
"""
Ensurethatthereareeitherloggersorplaceholdersalltheway
fromthespecifiedloggertotherootoftheloggerhierarchy.
"""
name=alogger.name#获取日志打印器名称
i=name.rfind(".")
rv=None#存放alogger日志打印器的上级日志打印器
while(i>0)andnotrv:#如果名称中存在英文的点,并且找到上级日志打印器
substr=name[:i]#获取名称中位于最后一个英文的点的左侧字符串(暂且称至为点分上级)
ifsubstrnotinself.loggerDict:#如果点分上级不存在日志打印器字典中
self.loggerDict[substr]=PlaceHolder(alogger)#创建PlaceHolder实例作为点分上级对应的日志打印器#继续查找点分上级日志打印器#注意,这里的PlaceHolder仅是占位用,不是真的打印器,这里为了方便描述,暂且称之为PlaceHolder日志打印器
else:#否则
obj=self.loggerDict[substr]#获取点分上级对应的日志打印器
ifisinstance(obj,Logger):#如果为Logger实例,如果是,则跳出循环,执行#为日志打印器设置上级
rv=obj
else:#否则
assertisinstance(obj,PlaceHolder)#断言它为PlaceHolder的实例
obj.append(alogger)#把日志打印器添加为点分上级对应的PlaceHolder日志打印器实例的下级日志打印器执行#继续查找点分上级日志打印器
i=name.rfind(".",0,i-1)#继续查找点分上级日志打印器
ifnotrv:#找不到点分上级、或者遍历完所有点分上级,都没找到上级日志打印器
rv=self.root#则把root日志打印器设置为alogger日志打印器的上级日志打印器
alogger.parent=rv#为日志打印器设置上级
def_fixupChildren(self,ph,alogger):
"""
Ensurethatchildrenoftheplaceholderphareconnectedtothe
specifiedlogger.
"""
name=alogger.name#获取日志打印器名称
namelen=len(name)#获取日志打印器名称长度
forcinph.loggerMap.keys():#遍历获取的PlaceHolder日志打印器实例的子级日志打印器
#Theifmeans...ifnotc.parent.name.startswith(nm)
ifc.parent.name[:namelen]!=name:#如果PlaceHolder日志打印器实例名称不以alogger日志打印器名称为前缀,
alogger.parent=c.parent#那么,设置alogger日志打印器的上级日志打印器为PlaceHolder日志打印器
c.parent=alogger#设置alogger日志打印器为PlaceHolder日志打印器原有下级PlaceHolder日志打印器的上级
结论:日志打印器都是分父子级的,这个父子层级是怎么形成的,参见上述函数代码注解
到此这篇关于Python日志打印里logging.getLogger源码分析详解的文章就介绍到这了,更多相关Pythonlogging.getLogger源码分析内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!