Python Logging 指南

Python Logging 指南

文章翻譯自官方文檔:Logging HOWTO

基礎(chǔ)日志教程

日志記錄是一種跟蹤某些軟件運行時發(fā)生的事件的方法。該軟件的開發(fā)人員將日志記錄調(diào)用添加到其代碼中窃躲,以指示已發(fā)生某些事件窟却。事件由描述性消息描述售滤,該消息可以可選地包含可變數(shù)據(jù)(即每次事件發(fā)生時可能不同的數(shù)據(jù))。事件也具有開發(fā)人員對事件的重要性茂翔;重要性也可以稱為水平或嚴重程度涵但。

何時使用日志

Logging 為簡單的日志記錄使用提供了一組便利功能凳兵。它們是?debug(),?info(),?warning(),?error()?和?critical()卓鹿。要確定何時使用日志記錄,請參閱下表留荔,其中列出了針對一組常見任務(wù)中的每個任務(wù)的最佳工具。

您要執(zhí)行的任務(wù)這項任務(wù)的最佳工具

顯示控制臺輸出澜倦,以便正常使用命令行腳本或程序print()

報告在程序正常運行期間發(fā)生的事件(例如聚蝶,用于狀態(tài)監(jiān)測或故障調(diào)查)logging.info()(或者?logging.debug()?用于非常詳細的輸出以用于診斷目的)

發(fā)出有關(guān)特定運行時事件的警告warnings.warn(): 在代碼庫中,如果問題是可以避免的藻治,則應(yīng)修改客戶端應(yīng)用程序以消除警告?

logging.warning(): 如果客戶端應(yīng)用程序無法處理該情況碘勉,但仍應(yīng)注意該事件

報告有關(guān)特定運行時事件的錯誤拋出異常

報告在不引發(fā)異常的情況下抑制錯誤(例如,長時間運行的服務(wù)器進程中的錯誤處理程序)logging.error(),?logging.exception()?或?logging.critical()?適用于特定錯誤和應(yīng)用程序域

日志函數(shù)以它們用于跟蹤的事件的級別或嚴重性命名桩卵。標準級別及其適用性描述如下(按嚴重程度遞增):

級別什么時候使用

DEBUG詳細信息验靡,通常僅在診斷問題時才有意義。

INFO確認事情按預(yù)期工作雏节。

WARNING表明發(fā)生了意外情況胜嗓,或表明在不久的將來出現(xiàn)了一些問題(例如 “磁盤空間不足”)。但是該軟件仍在按預(yù)期工作钩乍。

ERROR由于更嚴重的問題辞州,該軟件無法執(zhí)行某些功能。

CRITICAL嚴重錯誤寥粹,表明程序本身可能無法繼續(xù)運行变过。

默認級別為?WARNING?埃元,這意味著將僅跟蹤此級別及更高級別的事件,除非日志包已配置為執(zhí)行其他操作媚狰。

可以以不同方式處理被跟蹤的事件岛杀。處理跟蹤事件的最簡單方法是將它們打印到控制臺。另一種常見方法是將它們寫入磁盤文件崭孤。

一個簡單的例子

一個非常簡單的例子:

importlogginglogging.warning('Watch out!')# 將打印消息到控制臺logging.info('I told you so')# 不會打印任何東西復(fù)制代碼

如果您在腳本中輸入這幾行并運行它类嗤,您將看到:

WARNING:root:Watch out!復(fù)制代碼

打印在控制臺上。?INFO?消息不會出現(xiàn)裳瘪,因為默認級別為?WARNING?土浸。打印的消息包括記錄調(diào)用中提供的事件的級別和描述的指示,即 “Watch out!”彭羹。暫時不要擔心 'root' 部分:它將在后面解釋黄伊。如果需要,可以非常靈活地格式化實際輸出派殷;格式化選項也將在稍后解釋还最。

記錄到文件

一種非常常見的情況是在文件中記錄日志事件,所以讓我們看看下一步毡惜。請務(wù)必在新啟動的 Python 解釋器中嘗試以下操作拓轻,并且不要只繼續(xù)上述會話:

importlogginglogging.basicConfig(filename='example.log',level=logging.DEBUG)logging.debug('This message should go to the log file')logging.info('So should this')logging.warning('And this, too')復(fù)制代碼

現(xiàn)在,如果我們打開文件并查看我們的內(nèi)容经伙,我們應(yīng)該找到日志消息:

DEBUG:root:This message should go to thelogfileINFO:root:So should thisWARNING:root:And this, too復(fù)制代碼

此示例還說明了如何設(shè)置作為跟蹤閾值的日志記錄級別扶叉。在這種情況下,因為我們將閾值設(shè)置為?DEBUG?帕膜,所以打印了所有消息枣氧。

如果要從命令行選項設(shè)置日志記錄級別,例如:

--log=INFO復(fù)制代碼

并且你有一個變量?loglevel?為?--log?傳遞的參數(shù)的值垮刹,你可以使用:

getattr(logging, loglevel.upper())復(fù)制代碼

通過?loglevel?參數(shù)獲取您將傳遞給?basicConfig()?的值达吞。您可能還希望檢查用戶的輸入值,如下例所示:

# 假設(shè) loglevel 是從命令行參數(shù)中獲取的字符串值荒典。 轉(zhuǎn)換為大寫以允許用戶# 指定 --log=DEBUG 或 --log=debugnumeric_level = getattr(logging, loglevel.upper(),None)ifnotisinstance(numeric_level, int):raiseValueError('Invalid log level: %s'% loglevel)logging.basicConfig(level=numeric_level, ...)復(fù)制代碼

對?basicConfig()?的調(diào)用應(yīng)該在調(diào)用?debug()酪劫,info()?等之前進行。由于它是一次性的簡單配置工具寺董,只有第一次調(diào)用才會真正做事情:后續(xù)調(diào)用實際上是無效的覆糟。

如果多次運行上述腳本,則連續(xù)運行的消息將附加到文件?example.log?中遮咖。如果您希望每次運行重新開始搪桂,而不記住早期運行的消息,則可以指定?filemode?參數(shù),通過將上例中的調(diào)用更改為:

logging.basicConfig(filename='example.log', filemode='w', level=logging.DEBUG)復(fù)制代碼

輸出將與之前相同踢械,但不再附加日志文件酗电,因此早期運行的消息將丟失。

多個模塊中的日志記錄

如果您的程序包含多個模塊内列,這里有一個如何組織日志記錄的示例:

# myapp.pyimportloggingimportmylibdefmain():logging.basicConfig(filename='myapp.log', level=logging.INFO)? ? logging.info('Started')? ? mylib.do_something()? ? logging.info('Finished')if__name__ =='__main__':? ? main()復(fù)制代碼

# mylib.pyimportloggingdefdo_something():logging.info('Doing something')復(fù)制代碼

如果你運行?myapp.py撵术,你應(yīng)該在?myapp.log?中看到這個:

INFO:root:StartedINFO:root:Doing somethingINFO:root:Finished復(fù)制代碼

希望你們能看到。您可以使用?mylib.py?中的模式將此概括為多個模塊话瞧。請注意嫩与,對于這種簡單的使用模式,除了查看事件描述之外交排,僅僅通過查看日志文件划滋,您不會知道您的消息來自應(yīng)用程序中的何處。如果要跟蹤消息的位置埃篓,則需要參考教程級別之外的文檔 -- 請參閱高級日志教程处坪。

記錄變量數(shù)據(jù)

要記錄變量數(shù)據(jù),請使用格式字符串作為事件描述消息架专,并將變量數(shù)據(jù)作為參數(shù)附加同窘。例如:

importlogginglogging.warning('%s before you %s','Look','leap!')復(fù)制代碼

將顯示:

WARNING:root:Look before you leap!復(fù)制代碼

如您所見,將可變數(shù)據(jù)合并到事件描述消息中使用舊的?%?樣式字符串格式部脚。這是為了向后兼容:日志包也支持更新的格式化選項想邦,如?str.format()?和?string.Template。但探索它們超出了本教程的范圍委刘,相關(guān)信息請參閱 --?在整個應(yīng)用程序中使用特定格式樣式

更改顯示消息的格式

要更改用于顯示消息的格式丧没,您需要指定要使用的格式:

importlogginglogging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)logging.debug('This message should appear on the console')logging.info('So should this')logging.warning('And this, too')復(fù)制代碼

這會打印:

DEBUG:This message should appear on the consoleINFO:So should thisWARNING:And this, too復(fù)制代碼

請注意锡移,前面示例中出現(xiàn)的 “root” 已消失骂铁。對于可以出現(xiàn)在格式字符串中的一整套內(nèi)容,你可以參考?LogRecord 屬性的文檔罩抗,但為了簡單使用,您只需要?levelname(重要性)灿椅,message(事件描述套蒂,包括可變數(shù)據(jù)),并可能顯示事件發(fā)生的時間茫蛹。這將在下一節(jié)中介紹操刀。

在消息中顯示日期/時間

要顯示事件的日期和時間,您可以在格式字符串中放置?%(asctime)s:

importlogginglogging.basicConfig(format='%(asctime)s %(message)s')logging.warning('is when this event was logged.')復(fù)制代碼

應(yīng)該打印這樣的東西:

2010-12-12 11:41:42,612 is when this event was logged.復(fù)制代碼

日期/時間顯示的默認格式(如上所示)類似于 ISO8601 或?RFC 3339婴洼。如果您需要更多地控制日期/時間的格式骨坑,請為?basicConfig?提供?datefmt?參數(shù),如下例所示:

importlogginglogging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')logging.warning('is when this event was logged.')復(fù)制代碼

這會顯示如下:

12/12/2010 11:46:36 AM is when this event was logged.復(fù)制代碼

datefmt?參數(shù)的格式與?time.strftime()?支持的格式相同。

下一步

基本教程到此結(jié)束欢唾。它應(yīng)該足以讓您啟動并運行 logging且警。日志包提供了更多功能,但為了充分利用它礁遣,您需要花費更多的時間來閱讀以下部分斑芜。如果你準備好了,可以拿一些你最喜歡的飲料繼續(xù)祟霍。

如果您的日志記錄需求很簡單杏头,那么使用上面的示例將日志記錄合并到您自己的腳本中基本就可以了。

還在沸呐?您可以繼續(xù)閱讀接下來的幾個部分醇王,這些部分提供了比上面基本部分更高級/深入的教程。之后崭添,您可以查看?Logging Cookbook寓娩。

高級日志教程

日志庫采用模塊化方法,并提供幾類組件:記錄器 (loggers)滥朱,處理器 (handlers)根暑,過濾器 (filters) 和格式化器 (formatters)。

記錄器公開應(yīng)用程序代碼直接使用的接口徙邻。

處理器將日志(由記錄器創(chuàng)建)發(fā)送到適當?shù)哪繕恕?/p>

過濾器提供了更精細的設(shè)施排嫌,用于確定要輸出的日志記錄。

格式化器指定最終輸出中的日志記錄的布局缰犁。

日志事件信息在?LogRecord?實例中的記錄器淳地,處理器,過濾器和格式化器之間傳遞帅容。

通過在?Logger?類的實例(以下稱為記錄器)上調(diào)用方法來執(zhí)行日志記錄颇象。每個實例都有一個名稱,它們在概念上以點(句點)作為分隔符排列在命名空間層次結(jié)構(gòu)中并徘。例如遣钳,名為 “scan” 的記錄器是記錄器 'scan.text','scan.html' 和 'scan.pdf' 的父級麦乞。記錄器名稱可以是您想要的任何名稱蕴茴,并指明記錄消息來源的應(yīng)用程序區(qū)域。

在命名記錄器時使用的一個好習慣是在每個使用日志記錄的模塊中使用模塊級記錄器姐直,命名如下:

logger = logging.getLogger(__name__)復(fù)制代碼

這意味著記錄器名稱跟蹤包/模塊層次結(jié)構(gòu)倦淀,并且直觀地顯示從記錄器名稱記錄事件的位置。

記錄器層次結(jié)構(gòu)的根稱為根記錄器声畏。這是函數(shù)?debug()撞叽,info(),warning(),error()?和?critical()?使用的記錄器愿棋,它只調(diào)用根記錄器的同名方法科展。函數(shù)和方法具有相同的簽名。根記錄器的名稱在記錄的輸出中打印為 “root”初斑。

當然辛润,可以將消息記錄到不同的目的地。軟件包中包含支持见秤,用于將日志消息寫入文件砂竖,HTTP GET/POST 位置,通過 SMTP 發(fā)送電子郵件鹃答,通用套接字乎澄,隊列或特定于操作系統(tǒng)的日志記錄機制(如 syslog 或 Windows NT 事件日志)。目標由處理器類提供测摔。如果內(nèi)置處理器類未滿足你的特殊要求置济,則可以創(chuàng)建自己的日志目標類。

默認情況下锋八,沒有為任何日志記錄消息設(shè)置目標浙于。您可以使用?basicConfig()?指定目標(例如控制臺或文件),如前文中所示挟纱。如果調(diào)用函數(shù)?debug()羞酗,info(),warning()紊服,error()?和?critical()檀轨,它們將檢查是否沒有設(shè)置目標;如果未設(shè)置欺嗤,則在委派給根記錄器執(zhí)行實際消息輸出之前参萄,他們將設(shè)置控制臺的目標(sys.stderr)和顯示消息的默認格式。

basicConfig()?為消息設(shè)置的默認格式為:

severity:logger name:message復(fù)制代碼

您可以通過使用?format?關(guān)鍵字參數(shù)將格式字符串傳遞給?basicConfig()?來更改此設(shè)置煎饼。有關(guān)如何構(gòu)造格式字符串的所有選項讹挎,請參閱格式化對象

記錄流程

記錄器和處理器中的日志事件信息流程如下圖所示吆玖。

記錄器

Logger?對象有三重作業(yè)筒溃。首先,它們向應(yīng)用程序代碼公開了幾種方法衰伯,以便應(yīng)用程序可以在運行時記錄消息。其次积蔚,記錄器對象根據(jù)嚴重性(默認過濾工具)或過濾器對象確定要處理的日志消息意鲸。最后,記錄器對象將相關(guān)的日志消息傳遞給所有感興趣的日志處理器。

記錄器對象上使用最廣泛的方法分為兩類:配置和消息發(fā)送怎顾。

這些是最常見的配置方法:

Logger.setLevel()?指定記錄器將處理的日志級別读慎,其中?debug?是最低內(nèi)置日志級別,critical?是最高內(nèi)置日志級別槐雾。例如夭委,如果日志級別為 INFO,則記錄器將僅處理 INFO募强,WARNING株灸,ERROR 和 CRITICAL 消息,并將忽略 DEBUG 消息擎值。

Logger.addHandler()?和?Logger.removeHandler()?從記錄器對象中添加和刪除處理器對象慌烧。

Logger.addFilter()?和?Logger.removeFilter()?從記錄器對象中添加和刪除過濾器對象。

您不需要始終在您創(chuàng)建的每個記錄器上調(diào)用這些方法鸠儿。請參閱本節(jié)的最后兩段屹蚊。

配置?logger?對象后,以下方法將創(chuàng)建日志消息:

Logger.debug()进每,Logger.info()汹粤,Logger.warning(),Logger.error()?和?Logger.critical()?都創(chuàng)建日志記錄田晚,其中包含一條消息和一個與其各自方法名稱對應(yīng)的級別嘱兼。該消息實際上是一個格式字符串,可能包含?%s肉瓦,%d遭京,%f?的標準字符串替換語法,依此類推泞莉。其余參數(shù)是與消息中的替換字段對應(yīng)的對象列表哪雕。關(guān)于?**kwargs,日志記錄方法僅關(guān)注?exc_info?的關(guān)鍵字鲫趁,并使用它來確定是否記錄異常信息斯嚎。

Logger.exception()?創(chuàng)建類似于?Logger.error()?的日志消息。區(qū)別在于?Logger.exception()與其一起轉(zhuǎn)儲堆棧跟蹤挨厚。僅從異常處理程序調(diào)用此方法堡僻。

Logger.log()?將日志級別作為顯式參數(shù)。對于記錄消息而言疫剃,這比使用上面列出的日志級別便捷方法要詳細一些钉疫,但這可以自定義日志級別。

getLogger()?返回對具有指定名稱的記錄器實例的引用(如果已提供)巢价,如果不是則返回?root兵志。名稱是以句點分隔的層次結(jié)構(gòu)。對具有相同名稱的?getLogger()?的多次調(diào)用將返回對同一記錄器對象的引用或颊。在分層列表中較低的記錄器是列表中較高的記錄器的子項。例如备燃,給定一個名為?foo的記錄器,名稱為?foo.bar凌唬,foo.bar.baz?和?foo.bam?的記錄器都是?foo?的后代并齐。

記錄器具有有效級別的概念。如果未在記錄器上顯式設(shè)置級別客税,則使用其父級別作為其有效級別况褪。如果父級沒有明確的級別設(shè)置,則再檢查其父級霎挟,依此類推 - 搜索所有祖先窝剖,直到找到明確設(shè)置的級別。根記錄器始終設(shè)置了顯式級別(默認情況下為 WARNING)酥夭。在決定是否處理事件時赐纱,記錄器的有效級別用于確定事件是否傳遞給記錄器的處理器。

子記錄器將消息傳播到與其祖先記錄器相關(guān)聯(lián)的處理器熬北。因此疙描,不必為應(yīng)用程序使用的所有記錄器定義和配置處理器。為頂級記錄器配置處理器并根據(jù)需要創(chuàng)建子記錄器就足夠了讶隐。(但是起胰,您可以通過將記錄器的?propagate?屬性設(shè)置為?False?來關(guān)閉傳播。)

處理器

處理器對象負責將適當?shù)娜罩鞠ⅲɑ谌罩鞠⒌膰乐匦裕┓峙山o處理器的指定目標巫延。Logger對象可以使用?addHandler()?方法向自身添加零個或多個處理器對象效五。作為示例場景,應(yīng)用程序可能希望將所有日志消息發(fā)送到日志文件炉峰,將錯誤或更高的所有日志消息發(fā)送到標準輸出畏妖,以及將至關(guān)重要的所有消息發(fā)送到電子郵箱。此方案需要三個單獨的處理器疼阔,其中每個處理器負責將特定嚴重性的消息發(fā)送到特定位置戒劫。

標準庫包含很多處理器類型(請參閱常用處理器);這些教程在其示例中主要使用?StreamHandler和?FileHandler婆廊。

處理器中很少有方法需要應(yīng)用程序開發(fā)人員關(guān)注迅细。與使用內(nèi)置處理器對象(即不創(chuàng)建自定義處理器)的應(yīng)用程序開發(fā)人員相關(guān)的唯一處理器方法是以下配置方法:

與記錄器對象一樣,setLevel()?方法指定將分派到適當目標的最低嚴重性淘邻。為什么有兩個?setLevel()?方法茵典?記錄器中設(shè)置的級別確定將傳遞給其處理器的消息的嚴重性。而每個處理器中設(shè)置的級別確定處理器將發(fā)送哪些消息宾舅。

setFormatter()?選擇要使用的此處理器的 Formatter 對象统阿。

addFilter()?和?removeFilter()?分別在處理器上配置和取消配置過濾器對象枚尼。

應(yīng)用程序代碼不應(yīng)直接實例化和使用?Handler?的實例。相反砂吞,Handler?類是一個基類,它定義了所有處理程序應(yīng)具有的接口崎溃,并建立了子類可以使用(或覆蓋)的一些默認行為蜻直。

格式化器

Formatter?對象配置日志消息的最終順序,結(jié)構(gòu)和內(nèi)容袁串。與基本?logging.Handler?類不同概而,應(yīng)用程序代碼可以實例化?formatter?類,但如果應(yīng)用程序需要特殊行為囱修,則可能會對?formatter?進行子類化赎瑰。構(gòu)造函數(shù)有三個可選參數(shù) - 消息格式字符串,日期格式字符串和樣式指示符破镰。

logging.Formatter.__init__(fmt=None, datefmt=None, style='%')

如果沒有消息格式字符串餐曼,則默認使用原始消息。如果沒有日期格式字符串鲜漩,則默認日期格式為:

%Y-%m-%d %H:%M:%S復(fù)制代碼

最后加上毫秒數(shù)源譬。樣式是?%,'{'?或?'$'?之一孕似。如果未指定其中一個踩娘,則使用?'%'。

如果?style?為?'%'喉祭,則消息格式字符串使用?%(<dictionary key>)s?樣式字符串替換养渴;LogRecord 屬性中記錄了可能的鍵。如果?style?為 “{”泛烙,則假定消息格式字符串與?str.format()(使用關(guān)鍵字參數(shù))兼容理卑,而如果?style?為 “$”,則消息格式字符串應(yīng)符合?string.Template.substitute()?的預(yù)期胶惰。

Python 3.2 中添加了?style?參數(shù)傻工。

以下消息格式字符串將按以下順序以人類可讀的格式記錄時間,消息的嚴重性和消息的內(nèi)容:

'%(asctime)s - %(levelname)s - %(message)s'復(fù)制代碼

Formatters 使用用戶可配置的函數(shù)將記錄的創(chuàng)建時間轉(zhuǎn)換為元組孵滞。默認情況下中捆,使用?time.localtime();要為特定格式化器實例更改此值坊饶,請將實例的?converter?屬性設(shè)置為與?time.localtime()?或?time.gmtime()?具有相同簽名的函數(shù)泄伪。要為所有格式化程序更改它,例如匿级,如果要在 GMT 中顯示所有記錄時間蟋滴,請在 Formatter 類中設(shè)置?converter?屬性(用 GMT 則顯示?time.gmtime)染厅。

配置日志記錄

程序員可以通過三種方式配置日志記錄:

調(diào)用上面列出的配置方法顯式創(chuàng)建記錄器,處理器和格式化器津函。

創(chuàng)建日志配置文件并使用?fileConfig()?函數(shù)讀取它肖粮。

創(chuàng)建配置信息字典并將其傳遞給?dictConfig()?函數(shù)。

有關(guān)最后兩個選項的參考文檔尔苦,請參閱?配置函數(shù)涩馆。以下示例使用 Python 代碼配置一個非常簡單的記錄器,一個控制臺處理器和一個簡單的格式化器:

importlogging# create loggerlogger = logging.getLogger('simple_example')logger.setLevel(logging.DEBUG)# 創(chuàng)建控制臺處理器并設(shè)置級別進行調(diào)試ch = logging.StreamHandler()ch.setLevel(logging.DEBUG)# create formatterformatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')# add formatter to chch.setFormatter(formatter)# add ch to loggerlogger.addHandler(ch)# 'application' codelogger.debug('debug message')logger.info('info message')logger.warn('warn message')logger.error('error message')logger.critical('critical message')復(fù)制代碼

從命令行運行此模塊將生成以下輸出:

$ python simple_logging_module.py2005-03-1915:10:26,618- simple_example - DEBUG - debug message2005-03-1915:10:26,620- simple_example - INFO - info message2005-03-1915:10:26,695- simple_example - WARNING - warn message2005-03-1915:10:26,697- simple_example - ERROR - error message2005-03-1915:10:26,773- simple_example - CRITICAL - critical message復(fù)制代碼

以下 Python 模塊創(chuàng)建的記錄器允坚,處理器和格式化器與上面列出的示例幾乎完全相同魂那,唯一的區(qū)別是對象的名稱:

importloggingimportlogging.configlogging.config.fileConfig('logging.conf')# create loggerlogger = logging.getLogger('simpleExample')# 'application' codelogger.debug('debug message')logger.info('info message')logger.warn('warn message')logger.error('error message')logger.critical('critical message')復(fù)制代碼

這是?logging.conf?文件:

[loggers]keys=root,simpleExample[handlers]keys=consoleHandler[formatters]keys=simpleFormatter[logger_root]level=DEBUGhandlers=consoleHandler[logger_simpleExample]level=DEBUGhandlers=consoleHandlerqualname=simpleExamplepropagate=0[handler_consoleHandler]class=StreamHandlerlevel=DEBUGformatter=simpleFormatterargs=(sys.stdout,)[formatter_simpleFormatter]format=%(asctime)s - %(name)s - %(levelname)s - %(message)sdatefmt=復(fù)制代碼

輸出幾乎與基于非配置文件的示例相同:

$ python simple_logging_config.py2005-03-19 15:38:55,977 - simpleExample - DEBUG - debug message2005-03-19 15:38:55,979 - simpleExample - INFO - info message2005-03-19 15:38:56,054 - simpleExample - WARNING - warn message2005-03-19 15:38:56,055 - simpleExample - ERROR - error message2005-03-19 15:38:56,130 - simpleExample - CRITICAL - critical message復(fù)制代碼

您可以看到配置文件方法與 Python 代碼方法相比具有一些優(yōu)勢,主要是配置和代碼的分離以及非編碼器輕松修改日志記錄屬性的能力稠项。

!> 警告:fileConfig()?函數(shù)采用默認參數(shù)?disable_existing_loggers?涯雅,出于向后兼容的原因,默認參數(shù)為?True展运。這可能是您想要的活逆,也可能不是,因為它會導致在?fileConfig()?調(diào)用之前存在的任何記錄器被禁用拗胜,除非它們(或祖先)在配置中明確命名琴庵。有關(guān)詳細信息咖杂,請參閱參考文檔壤圃,如果需要踊挠,請為此參數(shù)指定?False。?

傳遞給dictConfig()?的字典也可以使用鍵?disable_existing_loggers?指定一個布爾值沪猴,如果未在字典中明確指定共缕,則默認情況下將其解釋為?True菠镇。這會導致上面描述的記錄器禁用行為隘梨,這可能不是您想要的 - 在這種情況下进萄,請顯式提供值為?False?的鍵援雇。

請注意,配置文件中引用的類名稱需要相對于日志記錄模塊剂习,或者可以使用常規(guī)導入機制解析的絕對值失仁。因此,您可以使用?WatchedFileHandler(相對于日志記錄模塊)或?mypackage.mymodule.MyHandler(對于在?mypackage?包和模塊?mymodule?中定義的類们何,其中?mypackage?在 Python 導入路徑上可用)

在 Python 3.2 中萄焦,引入了一種新的配置日志記錄的方法,使用字典來保存配置信息冤竹。這提供了上面概述的基于配置文件的方法的功能的超集拂封,并且是新應(yīng)用程序和部署的推薦配置方法。因為 Python 字典用于保存配置信息鹦蠕,并且由于您可以使用不同的方式填充該字典冒签,因此您有更多的配置選項。例如钟病,您可以使用 JSON 格式的配置文件萧恕,或者,如果您有權(quán)訪問 YAML 處理函數(shù)肠阱,則可以使用 YAML 格式的文件來填充配置字典廊鸥。或者辖所,當然惰说,您可以在 Python 代碼中構(gòu)建字典,通過套接字以序列化形式接收它缘回,或者使用對您的應(yīng)用程序有意義的任何方法吆视。

以下是與上述相同配置的示例,采用YAML格式酥宴,用于新的基于字典的方法:

version:1formatters:? simple:? ? format:'%(asctime)s - %(name)s - %(levelname)s - %(message)s'handlers:? console:class:logging.StreamHandler? ? level: DEBUG? ? formatter: simple? ? stream: ext://sys.stdoutloggers:? simpleExample:? ? level: DEBUG? ? handlers: [console]? ? propagate: noroot:? level: DEBUG? handlers: [console]復(fù)制代碼

有關(guān)使用字典進行日志記錄的詳細信息啦吧,請參閱?配置函數(shù)

如果沒有提供配置會發(fā)生什么

如果未提供日志記錄配置拙寡,則可能出現(xiàn)需要輸出日志記錄事件但無法找到輸出事件的處理器的情況授滓。在這些情況下,日志包的行為取決于 Python 版本。

對于 3.2 之前的 Python 版本般堆,行為如下:

如果?logging.raiseExceptions?為?False(生產(chǎn)模式)在孝,則會以靜默方式刪除該事件。

如果?logging.raiseExceptions?為?True(開發(fā)模式)淮摔,則會打印一條消息 “無法找到記錄器?X.Y.Z?的處理器”私沮。

在 Python 3.2 及更高版本中,行為如下:

該事件使用 “最后的處理器” 輸出和橙,存儲在?logging.lastResort?中仔燕。此內(nèi)部處理器不與任何記錄器關(guān)聯(lián),并且像?StreamHandler?一樣將事件描述消息寫入?sys.stderr?的當前值(因此尊重可能有效的任何重定向)魔招。沒有對消息進行格式化 - 只打印裸事件描述消息晰搀。處理程序的級別設(shè)置為?WARNING,因此將輸出此級別和更高級別的所有事件办斑。

要獲取 3.2 之前的行為外恕,logging.lastResort?可以設(shè)置為?None。

為庫配置日志記錄

在開發(fā)使用日志記錄的庫時俄周,您應(yīng)該注意記錄庫如何使用日志記錄 - 例如,使用的記錄器的名稱髓迎。還需要考慮其日志記錄配置峦朗。如果應(yīng)用程序不使用日志記錄,并且?guī)齑a進行日志記錄調(diào)用排龄,則(如上一節(jié)所述)嚴重性為?WARNING?和更高的事件將打印到?sys.stderr波势。這被認為是最好的默認行為。

如果由于某種原因您不希望在沒有任何日志記錄配置的情況下打印這些消息橄维,則可以將無操作處理器附加到庫的頂級記錄器尺铣。這樣可以避免打印消息,因為將始終為庫的事件找到處理器:它不會產(chǎn)生任何輸出争舞。如果庫用戶配置日志以供應(yīng)用程序使用凛忿,可能是配置將添加一些處理器,如果級別已適當配置竞川,則在庫代碼中進行的日志記錄調(diào)用將正常地將輸出發(fā)送給這些處理器店溢。

日志包中包含一個什么都不做的處理器:NullHandler(自 Python 3.1 起)∥冢可以將此處理程序的實例添加到庫使用的日志記錄命名空間的頂級記錄器中(如果要在沒有日志記錄配置的情況下阻止將庫的記錄事件輸出到?sys.stderr)床牧。如果庫?foo?的所有日志記錄都是使用名稱匹配?'foo.x','foo.x.y'?等的記錄器完成的遭贸,那么代碼:

importlogginglogging.getLogger('foo').addHandler(logging.NullHandler())復(fù)制代碼

應(yīng)該有所期望的效果戈咳。如果組織生成了許多庫,則指定的記錄器名稱可以是 “orgname.foo” 而不僅僅是 “foo”。

?> 注意:強烈建議您不要將?NullHandler?以外的任何處理程序添加到庫的記錄器中著蛙。這是因為處理器的配置是使用您的庫的應(yīng)用程序開發(fā)人員的特權(quán)删铃。用程序開發(fā)人員了解他們的目標受眾以及哪些處理器最適合他們的應(yīng)用程序:如果你在引擎蓋下添加處理器,你可能會干擾他們執(zhí)行單元測試和提供符合他們要求的日志的能力册踩。

記錄級別

日志記錄級別的數(shù)值在下表中給出泳姐。如果要定義自己的級別,并且需要它們具有相對于預(yù)定義級別的特定值暂吉,則主要關(guān)注這些級別胖秒。如果您使用相同的數(shù)值定義級別,它將覆蓋預(yù)定義的值慕的;預(yù)定義的名稱將丟失阎肝。

級別數(shù)值

CRITICAL50

ERROR40

WARNING30

INFO20

DEBUG10

NOTSET0

級別也可以與記錄器相關(guān)聯(lián),由開發(fā)人員或通過加載已保存的日志記錄配置來設(shè)置肮街。在記錄器上調(diào)用日志記錄方法時风题,記錄器會將其自身級別與方法調(diào)用關(guān)聯(lián)的級別進行比較。如果記錄器的級別高于方法調(diào)用的級別嫉父,則實際上不會生成任何記錄消息沛硅。這是控制日志輸出詳細程度的基本機制。

記錄消息被編碼為?LogRecord?類的實例绕辖。當記錄器決定實際記錄事件時摇肌,將從記錄消息創(chuàng)建?LogRecord?實例。

記錄消息通過使用處理器來處理調(diào)度機制仪际,處理器是?Handler?類的子類的實例围小。處理器負責確保記錄的消息(以?LogRecord?的形式)最終位于特定位置(或一組位置),這對于該消息的目標受眾是有用的(例如最終用戶树碱,支持服務(wù)臺員工肯适,系統(tǒng)管理員,開發(fā)人員)成榜。處理器傳遞用于特定目標的?LogRecord?實例框舔。每個記錄器可以有零個,一個或多個與之關(guān)聯(lián)的處理器(通過?Logger?的?addHandler()?方法)赎婚。除了與記錄器直接關(guān)聯(lián)的任何處理器之外雨饺,還會調(diào)用與記錄器的所有祖先關(guān)聯(lián)的所有處理器來分派消息(除非記錄器的?propagate?標志設(shè)置為?False?值,此時傳遞給祖先處理程序停止)惑淳。

就像記錄器一樣额港,處理器可以具有與它們相關(guān)聯(lián)的級別。處理器的級別充當過濾器歧焦,其方式與記錄器級別相同移斩。如果處理器決定實際調(diào)度事件肚医,則使用?emit()?方法將消息發(fā)送到其目標。大多數(shù)用戶定義的?Handler?子類都需要覆蓋此?emit()向瓷。

自定義級別

定義您自己的級別是可以的肠套,但不一定是必要的,因為現(xiàn)有級別是根據(jù)實踐經(jīng)驗選擇的猖任。但是你稚,如果您確信需要自定義級別,則在執(zhí)行此操作時應(yīng)特別小心朱躺,如果您正在開發(fā)庫刁赖,則定義自定義級別可能是一個非常糟糕的主意。那是因為如果多個庫作者都定義了他們自己的自定義級別长搀,由于給定的數(shù)值對于不同的庫而言可能意味著不同的事物宇弛,因此有可能使用這些多個庫的日志輸出對于使用開發(fā)者來說難以控制和(或)解釋。

常用處理器

除了基本的?Handler?類之外源请,還提供了許多有用的子類:

StreamHandler?實例將消息發(fā)送到流(類文件對象)枪芒。

FileHandler?實例將消息發(fā)送到磁盤文件。

BaseRotatingHandler?是在某個點切割日志文件的處理器的基類谁尸。它并不意味著直接實例化舅踪。而是使用?RotatingFileHandler?或?TimedRotatingFileHandler。

RotatingFileHandler?實例將消息發(fā)送到磁盤文件良蛮,支持最大日志文件大小和日志文件切割抽碌。

TimedRotatingFileHandler?實例將消息發(fā)送到磁盤文件,以特定的時間間隔切割日志文件背镇。

SocketHandler?實例將消息發(fā)送到 TCP/IP 套接字咬展。從 3.4 開始泽裳,也支持 Unix 域套接字瞒斩。

DatagramHandler?實例將消息發(fā)送到 UDP 套接字。從 3.4 開始涮总,也支持 Unix 域套接字胸囱。

SMTPHandler?實例將消息發(fā)送到指定的電子郵件地址。

SysLogHandler?實例將消息發(fā)送到 Unix syslog 守護程序瀑梗,可以是在遠程計算機上烹笔。

NTEventLogHandler?實例將消息發(fā)送到 Windows NT/2000/XP 事件日志。

MemoryHandler?實例將消息發(fā)送到內(nèi)存中的緩沖區(qū)抛丽,只要滿足特定條件谤职,就會刷新內(nèi)存中的緩沖區(qū)。

HTTPHandler?實例使用 GET 或 POST 語義將消息發(fā)送到 HTTP 服務(wù)器亿鲜。

WatchedFileHandler?實例監(jiān)視他們要記錄的文件允蜈。如果文件發(fā)生更改,則會關(guān)閉該文件并使用文件名重新打開。此處理程序僅在類 Unix 系統(tǒng)上有用; Windows 不支持使用的基礎(chǔ)機制饶套。

QueueHandler?實例將消息發(fā)送到隊列漩蟆,例如隊列或多處理模塊中實現(xiàn)的隊列。

NullHandler?實例不會對錯誤消息執(zhí)行任何操作妓蛮。

NullHandler?怠李,StreamHandler?和?FileHandler?類在核心日志包中定義。其他處理程序在子模塊?logging.handlers?中定義蛤克。(還有另一個子模塊?logging.config捺癞,用于配置功能。)

記錄的消息被格式化以便通過?Formatter?類的實例進行呈現(xiàn)咖耘。它們使用適合與?%?運算符和字典一起使用的格式字符串進行初始化翘簇。

對于批量格式化多個消息,可以使用?BufferingFormatter?的實例儿倒。除了格式字符串(應(yīng)用于批處理中的每個消息)之外版保,還提供了標題和尾部格式字符串。

當基于記錄器級別和(或)處理器級別的過濾不夠時夫否,可以將過濾器的實例添加到?Logger?和?Handler?實例(通過他們的?addFilter()?方法)彻犁。在決定進一步處理消息之前,記錄器和處理器都會查詢其所有過濾器以獲取權(quán)限凰慈。如果任何過濾器返回 false 值汞幢,則不會進一步處理該消息。

基本的過濾器功能允許按特定的記錄器名稱進行過濾微谓。如果使用此功能森篷,則允許通過過濾器發(fā)送到指定記錄器及其子項的消息,并刪除所有其他消息豺型。

記錄期間引發(fā)的異常

日志包旨在吞噬登錄生產(chǎn)時發(fā)生的異常仲智。這樣可以在處理日志記錄事件時發(fā)生錯誤 - 例如記錄錯誤配置,網(wǎng)絡(luò)或其他類似錯誤 - 不要導致使用日志記錄的應(yīng)用程序過早終止姻氨。

永遠不會吞下?SystemExit?和?KeyboardInterrupt?異常钓辆。在?Handler?子類的?emit()?方法期間發(fā)生的其他異常將傳遞給其?handleError()?方法。

Handler?中?handleError()?的默認實現(xiàn)檢查是否設(shè)置了模塊級變量?raiseExceptions肴焊。如果設(shè)置前联,則會向?sys.stderr?打印回溯。如果未設(shè)置娶眷,則吞下異常似嗤。

?> 注意:raiseExceptions?的默認值為?True。這是因為在開發(fā)期間届宠,您通常希望收到發(fā)生的任何異常的通知烁落。建議您將生產(chǎn)使用的?raiseExceptions?設(shè)置為?False?壳咕。

使用任意對象作為消息

在前面的部分和示例中,假設(shè)記錄事件時傳遞的消息是字符串顽馋。但是谓厘,這不是唯一的可能性。您可以將任意對象作為消息傳遞寸谜,并且當日志記錄系統(tǒng)需要將其轉(zhuǎn)換為字符串表示時竟稳,將調(diào)用其?__str__()?方法。實際上熊痴,如果您愿意他爸,可以避免完全計算字符串表示 - 例如,?SocketHandler?通過序列化并通過線路發(fā)送事件來發(fā)出事件果善。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末诊笤,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子巾陕,更是在濱河造成了極大的恐慌讨跟,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鄙煤,死亡現(xiàn)場離奇詭異晾匠,居然都是意外死亡,警方通過查閱死者的電腦和手機梯刚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進店門凉馆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人亡资,你說我怎么就攤上這事澜共。” “怎么了锥腻?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵嗦董,是天一觀的道長。 經(jīng)常有香客問我旷太,道長展懈,這世上最難降的妖魔是什么销睁? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任供璧,我火速辦了婚禮,結(jié)果婚禮上冻记,老公的妹妹穿的比我還像新娘睡毒。我一直安慰自己,他們只是感情好冗栗,可當我...
    茶點故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布演顾。 她就那樣靜靜地躺著供搀,像睡著了一般。 火紅的嫁衣襯著肌膚如雪钠至。 梳的紋絲不亂的頭發(fā)上葛虐,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天,我揣著相機與錄音棉钧,去河邊找鬼屿脐。 笑死,一個胖子當著我的面吹牛宪卿,可吹牛的內(nèi)容都是我干的的诵。 我是一名探鬼主播,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼佑钾,長吁一口氣:“原來是場噩夢啊……” “哼西疤!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起休溶,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤代赁,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后兽掰,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體管跺,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年禾进,在試婚紗的時候發(fā)現(xiàn)自己被綠了豁跑。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡泻云,死狀恐怖艇拍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情宠纯,我是刑警寧澤卸夕,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站婆瓜,受9級特大地震影響快集,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜廉白,卻給世界環(huán)境...
    茶點故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一个初、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧猴蹂,春花似錦院溺、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽逐虚。三九已至,卻和暖如春谆膳,著一層夾襖步出監(jiān)牢的瞬間叭爱,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工漱病, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留涤伐,地道東北人。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓缨称,卻偏偏與公主長得像凝果,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子睦尽,可洞房花燭夜當晚...
    茶點故事閱讀 45,630評論 2 359

推薦閱讀更多精彩內(nèi)容

  • From:Python之日志處理(logging模塊) - 云游道士 - 博客園 https://www.cnbl...
    vigny的先生閱讀 2,688評論 3 5
  • 在應(yīng)用程序中添加日志記錄總的來說基于三個目的:監(jiān)視代碼中變量的變化情況器净,周期性的記錄到文件中供其他應(yīng)用進行統(tǒng)計分析...
    時待吾閱讀 5,057評論 1 13
  • 本文翻譯自logging howto 基礎(chǔ)教程 日志是跟蹤軟件運行時發(fā)生事件的一種手段。Python開發(fā)者在代碼中...
    大蟒傳奇閱讀 4,257評論 0 17
  • 基于多目標規(guī)劃的資本結(jié)構(gòu)優(yōu)化模型理論研究 楊琴(安徽商貿(mào)職業(yè)技術(shù)學院 會計系,安徽 蕪湖 241002) **摘要...
    zhaohaoying閱讀 403評論 0 0
  • 離大學畢業(yè)僅有一年当凡,如今我雖在外交換山害,卻也感受到了各奔前程的緊張與焦慮。 這幾個是我大學最好的同學...
    笑我年少與輕狂閱讀 578評論 0 1