Python Logging 指南

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

基礎日誌教程

日誌記錄是一種跟蹤某些軟件運行時發生的事件的方法。該軟件的開發人員將日誌記錄調用添加到其代碼中,以指示已發生某些事件。事件由描述性消息描述,該消息能夠可選地包含可變數據(即每次事件發生時可能不一樣的數據)。事件也具備開發人員對事件的重要性;重要性也能夠稱爲水平或嚴重程度。python

什麼時候使用日誌

Logging 爲簡單的日誌記錄使用提供了一組便利功能。它們是 debug(), info(), warning(), error()critical()。要肯定什麼時候使用日誌記錄,請參閱下表,其中列出了針對一組常見任務中的每一個任務的最佳工具。程序員

您要執行的任務 這項任務的最佳工具
顯示控制檯輸出,以便正常使用命令行腳本或程序 print()
報告在程序正常運行期間發生的事件(例如,用於狀態監測或故障調查) logging.info()(或者 logging.debug() 用於很是詳細的輸出以用於診斷目的)
發出有關特定運行時事件的警告 warnings.warn(): 在代碼庫中,若是問題是能夠避免的,則應修改客戶端應用程序以消除警告

logging.warning(): 若是客戶端應用程序沒法處理該狀況,但仍應注意該事件
報告有關特定運行時事件的錯誤 拋出異常
報告在不引起異常的狀況下抑制錯誤(例如,長時間運行的服務器進程中的錯誤處理程序) logging.error(), logging.exception()logging.critical() 適用於特定錯誤和應用程序域

日誌函數以它們用於跟蹤的事件的級別或嚴重性命名。標準級別及其適用性描述以下(按嚴重程度遞增):api

級別 何時使用
DEBUG 詳細信息,一般僅在診斷問題時纔有意義。
INFO 確認事情按預期工做。
WARNING 代表發生了意外狀況,或代表在不久的未來出現了一些問題(例如 「磁盤空間不足」)。可是該軟件仍在按預期工做。
ERROR 因爲更嚴重的問題,該軟件沒法執行某些功能。
CRITICAL 嚴重錯誤,代表程序自己可能沒法繼續運行。

默認級別爲 WARNING ,這意味着將僅跟蹤此級別及更高級別的事件,除非日誌包已配置爲執行其餘操做。bash

能夠以不一樣方式處理被跟蹤的事件。處理跟蹤事件的最簡單方法是將它們打印到控制檯。另外一種常見方法是將它們寫入磁盤文件。服務器

一個簡單的例子

一個很是簡單的例子:網絡

import logging
logging.warning('Watch out!')  # 將打印消息到控制檯
logging.info('I told you so')  # 不會打印任何東西
複製代碼

若是您在腳本中輸入這幾行並運行它,您將看到:app

WARNING:root:Watch out!
複製代碼

打印在控制檯上。 INFO 消息不會出現,由於默認級別爲 WARNING 。打印的消息包括記錄調用中提供的事件的級別和描述的指示,即 「Watch out!」。暫時不要擔憂 'root' 部分:它將在後面解釋。若是須要,能夠很是靈活地格式化實際輸出;格式化選項也將在稍後解釋。模塊化

記錄到文件

一種很是常見的狀況是在文件中記錄日誌事件,因此讓咱們看看下一步。請務必在新啓動的 Python 解釋器中嘗試如下操做,而且不要只繼續上述會話:函數

import logging
logging.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')
複製代碼

如今,若是咱們打開文件並查看咱們的內容,咱們應該找到日誌消息:

DEBUG:root:This message should go to the log file
INFO:root:So should this
WARNING:root:And this, too
複製代碼

此示例還說明了如何設置做爲跟蹤閾值的日誌記錄級別。在這種狀況下,由於咱們將閾值設置爲 DEBUG ,因此打印了全部消息。

若是要從命令行選項設置日誌記錄級別,例如:

--log=INFO
複製代碼

而且你有一個變量 loglevel--log 傳遞的參數的值,你可使用:

getattr(logging, loglevel.upper())
複製代碼

經過 loglevel 參數獲取您將傳遞給 basicConfig() 的值。您可能還但願檢查用戶的輸入值,以下例所示:

# 假設 loglevel 是從命令行參數中獲取的字符串值。 轉換爲大寫以容許用戶
# 指定 --log=DEBUG 或 --log=debug
numeric_level = getattr(logging, loglevel.upper(), None)
if not isinstance(numeric_level, int):
    raise ValueError('Invalid log level: %s' % loglevel)
logging.basicConfig(level=numeric_level, ...)
複製代碼

basicConfig() 的調用應該在調用 debug()info() 等以前進行。因爲它是一次性的簡單配置工具,只有第一次調用纔會真正作事情:後續調用其實是無效的。

若是屢次運行上述腳本,則連續運行的消息將附加到文件 example.log 中。若是您但願每次運行從新開始,而不記住早期運行的消息,則能夠指定 filemode 參數,經過將上例中的調用更改成:

logging.basicConfig(filename='example.log', filemode='w', level=logging.DEBUG)
複製代碼

輸出將與以前相同,但再也不附加日誌文件,所以早期運行的消息將丟失。

多個模塊中的日誌記錄

若是您的程序包含多個模塊,這裏有一個如何組織日誌記錄的示例:

# myapp.py
import logging
import mylib

def main():
    logging.basicConfig(filename='myapp.log', level=logging.INFO)
    logging.info('Started')
    mylib.do_something()
    logging.info('Finished')

if __name__ == '__main__':
    main()
複製代碼
# mylib.py
import logging

def do_something():
    logging.info('Doing something')
複製代碼

若是你運行 myapp.py,你應該在 myapp.log 中看到這個:

INFO:root:Started
INFO:root:Doing something
INFO:root:Finished
複製代碼

但願大家能看到。您可使用 mylib.py 中的模式將此歸納爲多個模塊。請注意,對於這種簡單的使用模式,除了查看事件描述以外,僅僅經過查看日誌文件,您不會知道您的消息來自應用程序中的何處。若是要跟蹤消息的位置,則須要參考教程級別以外的文檔 -- 請參閱高級日誌教程

記錄變量數據

要記錄變量數據,請使用格式字符串做爲事件描述消息,並將變量數據做爲參數附加。例如:

import logging
logging.warning('%s before you %s', 'Look', 'leap!')
複製代碼

將顯示:

WARNING:root:Look before you leap!
複製代碼

如您所見,將可變數據合併到事件描述消息中使用舊的 % 樣式字符串格式。這是爲了向後兼容:日誌包也支持更新的格式化選項,如 str.format()string.Template。但探索它們超出了本教程的範圍,相關信息請參閱 -- 在整個應用程序中使用特定格式樣式

更改顯示消息的格式

要更改用於顯示消息的格式,您須要指定要使用的格式:

import logging
logging.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')
複製代碼

這會打印:

DEBUG:This message should appear on the console
INFO:So should this
WARNING:And this, too
複製代碼

請注意,前面示例中出現的 「root」 已消失。對於能夠出如今格式字符串中的一整套內容,你能夠參考 LogRecord 屬性的文檔,但爲了簡單使用,您只須要 levelname(重要性),message(事件描述,包括可變數據),並可能顯示事件發生的時間。這將在下一節中介紹。

在消息中顯示日期/時間

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

import logging
logging.basicConfig(format='%(asctime)s %(message)s')
logging.warning('is when this event was logged.')
複製代碼

應該打印這樣的東西:

2010-12-12 11:41:42,612 is when this event was logged.
複製代碼

日期/時間顯示的默認格式(如上所示)相似於 ISO8601 或 RFC 3339。若是您須要更多地控制日期/時間的格式,請爲 basicConfig 提供 datefmt 參數,以下例所示:

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

這會顯示以下:

12/12/2010 11:46:36 AM is when this event was logged.
複製代碼

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

下一步

基本教程到此結束。它應該足以讓您啓動並運行 logging。日誌包提供了更多功能,但爲了充分利用它,您須要花費更多的時間來閱讀如下部分。若是你準備好了,能夠拿一些你最喜歡的飲料繼續。

若是您的日誌記錄需求很簡單,那麼使用上面的示例將日誌記錄合併到您本身的腳本中基本就能夠了。

還在?您能夠繼續閱讀接下來的幾個部分,這些部分提供了比上面基本部分更高級/深刻的教程。以後,您能夠查看 Logging Cookbook

高級日誌教程

日誌庫採用模塊化方法,並提供幾類組件:記錄器 (loggers),處理器 (handlers),過濾器 (filters) 和格式化器 (formatters)。

  • 記錄器公開應用程序代碼直接使用的接口。
  • 處理器將日誌(由記錄器建立)發送到適當的目標。
  • 過濾器提供了更精細的設施,用於肯定要輸出的日誌記錄。
  • 格式化器指定最終輸出中的日誌記錄的佈局。

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

經過在 Logger 類的實例(如下稱爲記錄器)上調用方法來執行日誌記錄。每一個實例都有一個名稱,它們在概念上以點(句點)做爲分隔符排列在命名空間層次結構中。例如,名爲 「scan」 的記錄器是記錄器 'scan.text','scan.html' 和 'scan.pdf' 的父級。記錄器名稱能夠是您想要的任何名稱,並指明記錄消息來源的應用程序區域。

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

logger = logging.getLogger(__name__)
複製代碼

這意味着記錄器名稱跟蹤包/模塊層次結構,而且直觀地顯示從記錄器名稱記錄事件的位置。

記錄器層次結構的根稱爲根記錄器。這是函數 debug()info()warning()error()critical() 使用的記錄器,它只調用根記錄器的同名方法。函數和方法具備相同的簽名。根記錄器的名稱在記錄的輸出中打印爲 「root」。

固然,能夠將消息記錄到不一樣的目的地。軟件包中包含支持,用於將日誌消息寫入文件,HTTP GET/POST 位置,經過 SMTP 發送電子郵件,通用套接字,隊列或特定於操做系統的日誌記錄機制(如 syslog 或 Windows NT 事件日誌)。目標由處理器類提供。若是內置處理器類未知足你的特殊要求,則能夠建立本身的日誌目標類。

默認狀況下,沒有爲任何日誌記錄消息設置目標。您可使用 basicConfig() 指定目標(例如控制檯或文件),如前文中所示。若是調用函數 debug()info()warning()error()critical(),它們將檢查是否沒有設置目標;若是未設置,則在委派給根記錄器執行實際消息輸出以前,他們將設置控制檯的目標(sys.stderr)和顯示消息的默認格式。

basicConfig() 爲消息設置的默認格式爲:

severity:logger name:message
複製代碼

您能夠經過使用 format 關鍵字參數將格式字符串傳遞給 basicConfig() 來更改此設置。有關如何構造格式字符串的全部選項,請參閱格式化對象

記錄流程

記錄器和處理器中的日誌事件信息流程以下圖所示。

記錄器

Logger 對象有三重做業。首先,它們嚮應用程序代碼公開了幾種方法,以便應用程序能夠在運行時記錄消息。其次,記錄器對象根據嚴重性(默認過濾工具)或過濾器對象肯定要處理的日誌消息。最後,記錄器對象將相關的日誌消息傳遞給全部感興趣的日誌處理器。

記錄器對象上使用最普遍的方法分爲兩類:配置和消息發送。

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

  • Logger.setLevel() 指定記錄器將處理的日誌級別,其中 debug 是最低內置日誌級別,critical 是最高內置日誌級別。例如,若是日誌級別爲 INFO,則記錄器將僅處理 INFO,WARNING,ERROR 和 CRITICAL 消息,並將忽略 DEBUG 消息。
  • Logger.addHandler()Logger.removeHandler() 從記錄器對象中添加和刪除處理器對象。
  • Logger.addFilter()Logger.removeFilter() 從記錄器對象中添加和刪除過濾器對象。

您不須要始終在您建立的每一個記錄器上調用這些方法。請參閱本節的最後兩段。

配置 logger 對象後,如下方法將建立日誌消息:

  • Logger.debug()Logger.info()Logger.warning()Logger.error()Logger.critical() 都建立日誌記錄,其中包含一條消息和一個與其各自方法名稱對應的級別。該消息其實是一個格式字符串,可能包含 %s%d%f 的標準字符串替換語法,依此類推。其他參數是與消息中的替換字段對應的對象列表。關於 **kwargs,日誌記錄方法僅關注 exc_info 的關鍵字,並使用它來肯定是否記錄異常信息。
  • Logger.exception() 建立相似於 Logger.error() 的日誌消息。區別在於 Logger.exception() 與其一塊兒轉儲堆棧跟蹤。僅從異常處理程序調用此方法。
  • Logger.log() 將日誌級別做爲顯式參數。對於記錄消息而言,這比使用上面列出的日誌級別便捷方法要詳細一些,但這能夠自定義日誌級別。

getLogger() 返回對具備指定名稱的記錄器實例的引用(若是已提供),若是不是則返回 root。名稱是以句點分隔的層次結構。對具備相同名稱的 getLogger() 的屢次調用將返回對同一記錄器對象的引用。在分層列表中較低的記錄器是列表中較高的記錄器的子項。例如,給定一個名爲 foo 的記錄器,名稱爲 foo.barfoo.bar.bazfoo.bam 的記錄器都是 foo 的後代。

記錄器具備有效級別的概念。若是未在記錄器上顯式設置級別,則使用其父級別做爲其有效級別。若是父級沒有明確的級別設置,則再檢查其父級,依此類推 - 搜索全部祖先,直到找到明確設置的級別。根記錄器始終設置了顯式級別(默認狀況下爲 WARNING)。在決定是否處理事件時,記錄器的有效級別用於肯定事件是否傳遞給記錄器的處理器。

子記錄器將消息傳播到與其祖先記錄器相關聯的處理器。所以,沒必要爲應用程序使用的全部記錄器定義和配置處理器。爲頂級記錄器配置處理器並根據須要建立子記錄器就足夠了。(可是,您能夠經過將記錄器的 propagate 屬性設置爲 False 來關閉傳播。)

處理器

處理器對象負責將適當的日誌消息(基於日誌消息的嚴重性)分派給處理器的指定目標。Logger 對象可使用 addHandler() 方法向自身添加零個或多個處理器對象。做爲示例場景,應用程序可能但願將全部日誌消息發送到日誌文件,將錯誤或更高的全部日誌消息發送到標準輸出,以及將相當重要的全部消息發送到電子郵箱。此方案須要三個單獨的處理器,其中每一個處理器負責將特定嚴重性的消息發送到特定位置。

標準庫包含不少處理器類型(請參閱經常使用處理器);這些教程在其示例中主要使用 StreamHandlerFileHandler

處理器中不多有方法須要應用程序開發人員關注。與使用內置處理器對象(即不建立自定義處理器)的應用程序開發人員相關的惟一處理器方法是如下配置方法:

  • 與記錄器對象同樣,setLevel() 方法指定將分派到適當目標的最低嚴重性。爲何有兩個 setLevel() 方法?記錄器中設置的級別肯定將傳遞給其處理器的消息的嚴重性。而每一個處理器中設置的級別肯定處理器將發送哪些消息。
  • setFormatter() 選擇要使用的此處理器的 Formatter 對象。
  • addFilter()removeFilter() 分別在處理器上配置和取消配置過濾器對象。

應用程序代碼不該直接實例化和使用 Handler 的實例。相反,Handler 類是一個基類,它定義了全部處理程序應具備的接口,並創建了子類可使用(或覆蓋)的一些默認行爲。

格式化器

Formatter 對象配置日誌消息的最終順序,結構和內容。與基本 logging.Handler 類不一樣,應用程序代碼能夠實例化 formatter 類,但若是應用程序須要特殊行爲,則可能會對 formatter 進行子類化。構造函數有三個可選參數 - 消息格式字符串,日期格式字符串和樣式指示符。

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

若是沒有消息格式字符串,則默認使用原始消息。若是沒有日期格式字符串,則默認日期格式爲:

%Y-%m-%d %H:%M:%S
複製代碼

最後加上毫秒數。樣式是 %'{''$' 之一。若是未指定其中一個,則使用 '%'

若是 style'%',則消息格式字符串使用 %(<dictionary key>)s 樣式字符串替換;LogRecord 屬性中記錄了可能的鍵。若是 style 爲 「{」,則假定消息格式字符串與 str.format()(使用關鍵字參數)兼容,而若是 style 爲 「$」,則消息格式字符串應符合 string.Template.substitute() 的預期。

Python 3.2 中添加了 style 參數。

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

'%(asctime)s - %(levelname)s - %(message)s'
複製代碼

Formatters 使用用戶可配置的函數將記錄的建立時間轉換爲元組。默認狀況下,使用 time.localtime();要爲特定格式化器實例更改此值,請將實例的 converter 屬性設置爲與 time.localtime()time.gmtime() 具備相同簽名的函數。要爲全部格式化程序更改它,例如,若是要在 GMT 中顯示全部記錄時間,請在 Formatter 類中設置 converter 屬性(用 GMT 則顯示 time.gmtime)。

配置日誌記錄

程序員能夠經過三種方式配置日誌記錄:

  1. 調用上面列出的配置方法顯式建立記錄器,處理器和格式化器。
  2. 建立日誌配置文件並使用 fileConfig() 函數讀取它。
  3. 建立配置信息字典並將其傳遞給 dictConfig() 函數。

有關最後兩個選項的參考文檔,請參閱 配置函數。如下示例使用 Python 代碼配置一個很是簡單的記錄器,一個控制檯處理器和一個簡單的格式化器:

import logging

# create logger
logger = logging.getLogger('simple_example')
logger.setLevel(logging.DEBUG)

# 建立控制檯處理器並設置級別進行調試
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)

# create formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# add formatter to ch
ch.setFormatter(formatter)

# add ch to logger
logger.addHandler(ch)

# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')
複製代碼

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

$ python simple_logging_module.py
2005-03-19 15:10:26,618 - simple_example - DEBUG - debug message
2005-03-19 15:10:26,620 - simple_example - INFO - info message
2005-03-19 15:10:26,695 - simple_example - WARNING - warn message
2005-03-19 15:10:26,697 - simple_example - ERROR - error message
2005-03-19 15:10:26,773 - simple_example - CRITICAL - critical message
複製代碼

如下 Python 模塊建立的記錄器,處理器和格式化器與上面列出的示例幾乎徹底相同,惟一的區別是對象的名稱:

import logging
import logging.config

logging.config.fileConfig('logging.conf')

# create logger
logger = logging.getLogger('simpleExample')

# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')
複製代碼

這是 logging.conf 文件:

[loggers]
keys=root,simpleExample

[handlers]
keys=consoleHandler

[formatters]
keys=simpleFormatter

[logger_root]
level=DEBUG
handlers=consoleHandler

[logger_simpleExample]
level=DEBUG
handlers=consoleHandler
qualname=simpleExample
propagate=0

[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)

[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=
複製代碼

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

$ python simple_logging_config.py
2005-03-19 15:38:55,977 - simpleExample - DEBUG - debug message
2005-03-19 15:38:55,979 - simpleExample - INFO - info message
2005-03-19 15:38:56,054 - simpleExample - WARNING - warn message
2005-03-19 15:38:56,055 - simpleExample - ERROR - error message
2005-03-19 15:38:56,130 - simpleExample - CRITICAL - critical message
複製代碼

您能夠看到配置文件方法與 Python 代碼方法相比具備一些優點,主要是配置和代碼的分離以及非編碼器輕鬆修改日誌記錄屬性的能力。

!> 警告:fileConfig() 函數採用默認參數 disable_existing_loggers ,出於向後兼容的緣由,默認參數爲 True。這多是您想要的,也可能不是,由於它會致使在 fileConfig() 調用以前存在的任何記錄器被禁用,除非它們(或祖先)在配置中明確命名。有關詳細信息,請參閱參考文檔,若是須要,請爲此參數指定 False

傳遞給dictConfig() 的字典也可使用鍵 disable_existing_loggers 指定一個布爾值,若是未在字典中明確指定,則默認狀況下將其解釋爲 True。這會致使上面描述的記錄器禁用行爲,這可能不是您想要的 - 在這種狀況下,請顯式提供值爲 False 的鍵。

請注意,配置文件中引用的類名稱須要相對於日誌記錄模塊,或者可使用常規導入機制解析的絕對值。所以,您可使用 WatchedFileHandler(相對於日誌記錄模塊)或 mypackage.mymodule.MyHandler(對於在 mypackage 包和模塊 mymodule 中定義的類,其中 mypackage 在 Python 導入路徑上可用)

在 Python 3.2 中,引入了一種新的配置日誌記錄的方法,使用字典來保存配置信息。這提供了上面概述的基於配置文件的方法的功能的超集,而且是新應用程序和部署的推薦配置方法。由於 Python 字典用於保存配置信息,而且因爲您可使用不一樣的方式填充該字典,所以您有更多的配置選項。例如,您可使用 JSON 格式的配置文件,或者,若是您有權訪問 YAML 處理函數,則可使用 YAML 格式的文件來填充配置字典。或者,固然,您能夠在 Python 代碼中構建字典,經過套接字以序列化形式接收它,或者使用對您的應用程序有意義的任何方法。

如下是與上述相同配置的示例,採用YAML格式,用於新的基於字典的方法:

version: 1
formatters:
  simple:
    format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
handlers:
  console:
    class: logging.StreamHandler
    level: DEBUG
    formatter: simple
    stream: ext://sys.stdout
loggers:
  simpleExample:
    level: DEBUG
    handlers: [console]
    propagate: no
root:
  level: DEBUG
  handlers: [console]
複製代碼

有關使用字典進行日誌記錄的詳細信息,請參閱 配置函數

若是沒有提供配置會發生什麼

若是未提供日誌記錄配置,則可能出現須要輸出日誌記錄事件但沒法找到輸出事件的處理器的狀況。在這些狀況下,日誌包的行爲取決於 Python 版本。

對於 3.2 以前的 Python 版本,行爲以下:

  • 若是 logging.raiseExceptionsFalse(生產模式),則會以靜默方式刪除該事件。
  • 若是 logging.raiseExceptionsTrue(開發模式),則會打印一條消息 「沒法找到記錄器 X.Y.Z 的處理器」。

在 Python 3.2 及更高版本中,行爲以下:

  • 該事件使用 「最後的處理器」 輸出,存儲在 logging.lastResort 中。此內部處理器不與任何記錄器關聯,而且像 StreamHandler 同樣將事件描述消息寫入 sys.stderr 的當前值(所以尊重可能有效的任何重定向)。沒有對消息進行格式化 - 只打印裸事件描述消息。處理程序的級別設置爲 WARNING,所以將輸出此級別和更高級別的全部事件。

要獲取 3.2 以前的行爲,logging.lastResort 能夠設置爲 None

爲庫配置日誌記錄

在開發使用日誌記錄的庫時,您應該注意記錄庫如何使用日誌記錄 - 例如,使用的記錄器的名稱。還須要考慮其日誌記錄配置。若是應用程序不使用日誌記錄,而且庫代碼進行日誌記錄調用,則(如上一節所述)嚴重性爲 WARNING 和更高的事件將打印到 sys.stderr。這被認爲是最好的默認行爲。

若是因爲某種緣由您不但願在沒有任何日誌記錄配置的狀況下打印這些消息,則能夠將無操做處理器附加到庫的頂級記錄器。這樣能夠避免打印消息,由於將始終爲庫的事件找處處理器:它不會產生任何輸出。若是庫用戶配置日誌以供應用程序使用,多是配置將添加一些處理器,若是級別已適當配置,則在庫代碼中進行的日誌記錄調用將正常地將輸出發送給這些處理器。

日誌包中包含一個什麼都不作的處理器:NullHandler(自 Python 3.1 起)。能夠將此處理程序的實例添加到庫使用的日誌記錄命名空間的頂級記錄器中(若是要在沒有日誌記錄配置的狀況下阻止將庫的記錄事件輸出到 sys.stderr)。若是庫 foo 的全部日誌記錄都是使用名稱匹配 'foo.x''foo.x.y' 等的記錄器完成的,那麼代碼:

import logging
logging.getLogger('foo').addHandler(logging.NullHandler())
複製代碼

應該有所指望的效果。若是組織生成了許多庫,則指定的記錄器名稱能夠是 「orgname.foo」 而不只僅是 「foo」。

?> 注意:強烈建議您不要將 NullHandler 之外的任何處理程序添加到庫的記錄器中。這是由於處理器的配置是使用您的庫的應用程序開發人員的特權。用程序開發人員瞭解他們的目標受衆以及哪些處理器最適合他們的應用程序:若是你在引擎蓋下添加處理器,你可能會干擾他們執行單元測試和提供符合他們要求的日誌的能力。

記錄級別

日誌記錄級別的數值在下表中給出。若是要定義本身的級別,而且須要它們具備相對於預約義級別的特定值,則主要關注這些級別。若是您使用相同的數值定義級別,它將覆蓋預約義的值;預約義的名稱將丟失。

級別 數值
CRITICAL 50
ERROR 40
WARNING 30
INFO 20
DEBUG 10
NOTSET 0

級別也能夠與記錄器相關聯,由開發人員或經過加載已保存的日誌記錄配置來設置。在記錄器上調用日誌記錄方法時,記錄器會將其自身級別與方法調用關聯的級別進行比較。若是記錄器的級別高於方法調用的級別,則實際上不會生成任何記錄消息。這是控制日誌輸出詳細程度的基本機制。

記錄消息被編碼爲 LogRecord 類的實例。當記錄器決定實際記錄事件時,將從記錄消息建立 LogRecord 實例。

記錄消息經過使用處理器來處理調度機制,處理器是 Handler 類的子類的實例。處理器負責確保記錄的消息(以 LogRecord 的形式)最終位於特定位置(或一組位置),這對於該消息的目標受衆是有用的(例如最終用戶,支持服務檯員工,系統管理員,開發人員)。處理器傳遞用於特定目標的 LogRecord 實例。每一個記錄器能夠有零個,一個或多個與之關聯的處理器(經過 LoggeraddHandler() 方法)。除了與記錄器直接關聯的任何處理器以外,還會調用與記錄器的全部祖先關聯的全部處理器來分派消息(除非記錄器的 propagate 標誌設置爲 False 值,此時傳遞給祖先處理程序中止)。

就像記錄器同樣,處理器能夠具備與它們相關聯的級別。處理器的級別充當過濾器,其方式與記錄器級別相同。若是處理器決定實際調度事件,則使用 emit() 方法將消息發送到其目標。大多數用戶定義的 Handler 子類都須要覆蓋此 emit()

自定義級別

定義您本身的級別是能夠的,但不必定是必要的,由於現有級別是根據實踐經驗選擇的。可是,若是您確信須要自定義級別,則在執行此操做時應特別當心,若是您正在開發庫,則定義自定義級別多是一個很是糟糕的主意。那是由於若是多個庫做者都定義了他們本身的自定義級別,因爲給定的數值對於不一樣的庫而言可能意味着不一樣的事物,所以有可能使用這些多個庫的日誌輸出對於使用開發者來講難以控制和(或)解釋。

經常使用處理器

除了基本的 Handler 類以外,還提供了許多有用的子類:

  1. StreamHandler 實例將消息發送到流(類文件對象)。
  2. FileHandler 實例將消息發送到磁盤文件。
  3. BaseRotatingHandler 是在某個點切割日誌文件的處理器的基類。它並不意味着直接實例化。而是使用 RotatingFileHandlerTimedRotatingFileHandler
  4. RotatingFileHandler 實例將消息發送到磁盤文件,支持最大日誌文件大小和日誌文件切割。
  5. TimedRotatingFileHandler 實例將消息發送到磁盤文件,以特定的時間間隔切割日誌文件。
  6. SocketHandler 實例將消息發送到 TCP/IP 套接字。從 3.4 開始,也支持 Unix 域套接字。
  7. DatagramHandler 實例將消息發送到 UDP 套接字。從 3.4 開始,也支持 Unix 域套接字。
  8. SMTPHandler 實例將消息發送到指定的電子郵件地址。
  9. SysLogHandler 實例將消息發送到 Unix syslog 守護程序,能夠是在遠程計算機上。
  10. NTEventLogHandler 實例將消息發送到 Windows NT/2000/XP 事件日誌。
  11. MemoryHandler 實例將消息發送到內存中的緩衝區,只要知足特定條件,就會刷新內存中的緩衝區。
  12. HTTPHandler 實例使用 GET 或 POST 語義將消息發送到 HTTP 服務器。
  13. WatchedFileHandler 實例監視他們要記錄的文件。若是文件發生更改,則會關閉該文件並使用文件名從新打開。此處理程序僅在類 Unix 系統上有用; Windows 不支持使用的基礎機制。
  14. QueueHandler 實例將消息發送到隊列,例如隊列或多處理模塊中實現的隊列。
  15. NullHandler 實例不會對錯誤消息執行任何操做。

NullHandlerStreamHandlerFileHandler 類在覈心日誌包中定義。其餘處理程序在子模塊 logging.handlers 中定義。(還有另外一個子模塊 logging.config,用於配置功能。)

記錄的消息被格式化以便經過 Formatter 類的實例進行呈現。它們使用適合與 % 運算符和字典一塊兒使用的格式字符串進行初始化。

對於批量格式化多個消息,可使用 BufferingFormatter 的實例。除了格式字符串(應用於批處理中的每一個消息)以外,還提供了標題和尾部格式字符串。

當基於記錄器級別和(或)處理器級別的過濾不夠時,能夠將過濾器的實例添加到 LoggerHandler 實例(經過他們的 addFilter() 方法)。在決定進一步處理消息以前,記錄器和處理器都會查詢其全部過濾器以獲取權限。若是任何過濾器返回 false 值,則不會進一步處理該消息。

基本的過濾器功能容許按特定的記錄器名稱進行過濾。若是使用此功能,則容許經過過濾器發送到指定記錄器及其子項的消息,並刪除全部其餘消息。

記錄期間引起的異常

日誌包旨在吞噬登陸生產時發生的異常。這樣能夠在處理日誌記錄事件時發生錯誤 - 例如記錄錯誤配置,網絡或其餘相似錯誤 - 不要致使使用日誌記錄的應用程序過早終止。

永遠不會吞下 SystemExitKeyboardInterrupt 異常。在 Handler 子類的 emit() 方法期間發生的其餘異常將傳遞給其 handleError() 方法。

HandlerhandleError() 的默認實現檢查是否設置了模塊級變量 raiseExceptions。若是設置,則會向 sys.stderr 打印回溯。若是未設置,則吞下異常。

?> 注意:raiseExceptions 的默認值爲 True。這是由於在開發期間,您一般但願收到發生的任何異常的通知。建議您將生產使用的 raiseExceptions 設置爲 False

使用任意對象做爲消息

在前面的部分和示例中,假設記錄事件時傳遞的消息是字符串。可是,這不是惟一的可能性。您能夠將任意對象做爲消息傳遞,而且當日志記錄系統須要將其轉換爲字符串表示時,將調用其 __str__() 方法。實際上,若是您願意,能夠避免徹底計算字符串表示 - 例如, SocketHandler 經過序列化並經過線路發送事件來發出事件。

相關文章
相關標籤/搜索