代碼解析python標準庫logging模塊
問題1:如何獲取caller的(文件名,行號,函數(shù)名)?
當(dāng)新增一條log記錄時,最終將調(diào)用Logger類的_log方法,這個方法首先會創(chuàng)建一個LogRecord對象。LogRecord對象需要(filename, lineno, funcname)參數(shù)信息。這是通過如下語句得到的:
fn, lno, func = self.findCaller()
findCaller內(nèi)容如下:
f = currentframe() #f是frame對象,每個方法調(diào)用生成一個frame對象,放在程序堆棧中。
if f is not None:
f = f.f_back
rv = "(unknown file)", 0, "(unknown function)"
while hasattr(f, "f_code"):
co = f.f_code #獲取code對象,它包含filename屬性,funcname屬性
filename = os.path.normcase(co.co_filename)
if filename == _srcfile: #_srcfile是這個模塊文件自己的文件名,當(dāng)文件名不再相同時
f = f.f_back # 得到外部調(diào)用者的frame,這就是需要的。
continue
rv = (filename, f.f_lineno, co.co_name)
break
return rvcurrentframe函數(shù)的定義:
def currentframe():
"""Return the frame object for the caller's stack frame."""
try:
raise Exception #拋出異常,將生成traceback對象,其中包含frame對象。
except:
#sys.exc_traceback.tb_frame當(dāng)前的frame, f_back調(diào)用著的frame
return sys.exc_traceback.tb_frame.f_back
#sys._getframe(3)返回的并不是當(dāng)前的frame,3應(yīng)該是計算好了的,減少循環(huán)的次數(shù),返回的是logger.error()的frame
if hasattr(sys, '_getframe'): currentframe = lambda: sys._getframe(3)問題2: Logger對象的層級,父子關(guān)系如何實現(xiàn)的?
首先,logging模塊中l(wèi)ogger層級關(guān)系是一個樹形關(guān)系的結(jié)構(gòu),這個關(guān)系的樹根是'root'。
root = RootLogger(WARNING) #RootLogger類是Logger的子類,無特殊功能,只是定義名字為‘root'。 Logger.root = root Logger.manager = Manager(Logger.root)
當(dāng)調(diào)用logging.getLogger(),用以獲取某個Logger時,如果參數(shù)為空,則返回‘root’。否則,調(diào)用Manager的getLogger()方法獲取Logger。
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 rootManager的getLogger()定義如下:
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
_acquireLock()
try:
if name in self.loggerDict:
rv = self.loggerDict[name]
if isinstance(rv, PlaceHolder):
ph = rv
rv = _loggerClass(name)
rv.manager = self
self.loggerDict[name] = rv
self._fixupChildren(ph, rv)
self._fixupParents(rv)
else:
rv = _loggerClass(name)
rv.manager = self
self.loggerDict[name] = rv
self._fixupParents(rv)
finally:
_releaseLock()
return rvManager對象中的loggerDict字典,存放logger名字和logger對象的映射關(guān)系。PlaceHolder類,是一個容器。
例如,名字為'sell'的PlaceHolder對象,首先還不存在'sell'的logger,然后,所以以'sell‘開頭的logger在這個對象內(nèi)都存在一個引用,如'sell.food','sell.cloth.china'等已有的logger對象。 當(dāng)調(diào)用getLogger()獲取一個未存在的logger時,如名字為'level1.level2', 首先創(chuàng)建一個名字為'level1.level2'的logger對象,并存于loggerDict中。然后,調(diào)用_fixupParents()。
_fixupParents()的作用:
在這個名字的層級鏈上,找到第一個logger對象,將其作為父親,并返回。鏈上不是logger對象的名字,創(chuàng)建一個PlaceHolder對象(如果未創(chuàng)建),將自己加入其中。
例如,新增‘level1.level2.level3’的logger,調(diào)用_fixupParents將創(chuàng)建一個名字為'level1.level2‘的PlaceHolder對象,創(chuàng)建一個名字為’level1‘的PlaceHolder對象,并將’level1.level2.level3‘這個logger分別加入以上兩個PlaceHolder對象容器內(nèi),將它的父親設(shè)定為’root‘。
總之,_fixupParents是使logger對象指向真正的父親節(jié)點(logger對象),并將logger自己加入到所有上層的PlaceHolder對象容器內(nèi)。
如果獲取一個名字已存在于loggerDict中,并且這個名字對應(yīng)的是一個先前創(chuàng)建的PlaceHolder對象。首先,創(chuàng)建一個對應(yīng)名字的logger對象。然后,調(diào)用_fixupChild(),修正這個PlaceHolder對象所包含的下游logger對象的父親。最后,調(diào)用_fixupParent(),作用與上一步相同。
父子層級關(guān)系,主要作用是,當(dāng)logger對象的propagate屬性值1(默認值)時,每條logRecord記錄都會傳給父logger處理。這樣可以只需要定義好‘root’根logger對象,其他的logger定義個名字,根據(jù)模塊名,類名等,然后綁定一個NullHandler。最后,所有的logRecord將交給’root‘統(tǒng)一處理處理。這是多模塊產(chǎn)生統(tǒng)一格式log的方式。
以上就是代碼解析python標準庫logging模塊的詳細內(nèi)容,更多關(guān)于python標準庫logging模塊的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Python實現(xiàn)mysql數(shù)據(jù)庫更新表數(shù)據(jù)接口的功能
這篇文章主要給大家介紹了關(guān)于Python如何實現(xiàn)mysql數(shù)據(jù)庫更新表數(shù)據(jù)接口功能的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-11-11
Python實現(xiàn)將內(nèi)容寫入文件的五種方法總結(jié)
本篇帶你詳細看一下python將內(nèi)容寫入文件的方法以及細節(jié),主要包括write()方法、writelines()?方法、print()?函數(shù)、使用?csv?模塊、使用?json?模塊,需要的可以參考一下2023-04-04
Python PyQt5實戰(zhàn)項目之文件拷貝器的具體實現(xiàn)詳解
PyQt5以一套Python模塊的形式來實現(xiàn)功能。它包含了超過620個類,600個方法和函數(shù)。本篇文章手把手帶你用PyQt5實現(xiàn)一個簡單的文件拷貝器,大家可以在過程中查缺補漏,提升水平2021-11-11

