logging --- Python 的日誌記錄工具¶
此模組定義了多個函式和類,為應用程式和庫實現了一個靈活的事件日誌記錄系統。
由標準庫模組提供日誌記錄 API 的主要好處是,所有 Python 模組都可以參與日誌記錄,因此你的應用程式日誌可以包含你自己的訊息以及來自第三方模組的訊息。
下面是一個慣用法的簡單示例
# myapp.py
import logging
import mylib
logger = logging.getLogger(__name__)
def main():
logging.basicConfig(filename='myapp.log', level=logging.INFO)
logger.info('Started')
mylib.do_something()
logger.info('Finished')
if __name__ == '__main__':
main()
# mylib.py
import logging
logger = logging.getLogger(__name__)
def do_something():
logger.info('Doing something')
如果執行 myapp.py,你應該在 myapp.log 中看到如下內容
INFO:__main__:Started
INFO:mylib:Doing something
INFO:__main__:Finished
這種慣用法的關鍵特點是,大多數程式碼只是簡單地使用 getLogger(__name__) 建立一個模組級別的 logger,並使用該 logger 進行任何需要的日誌記錄。這種方式簡潔,同時允許下游程式碼在需要時進行細粒度控制。記錄到模組級 logger 的訊息會被轉發到更高級別模組中 logger 的處理器,一直上傳到最高階的 logger,即根 logger;這種方法被稱為分層日誌記錄。
為了使日誌記錄有用,需要對其進行配置:為每個 logger 設定級別和目標,可能還需要更改特定模組的日誌記錄方式,通常基於命令列引數或應用程式配置。在大多數情況下,像上面那樣,只需要配置根 logger,因為所有較低級別的模組級 logger 最終都會將它們的訊息轉發到其處理器。 basicConfig() 提供了一種快速配置根 logger 的方法,可以處理許多用例。
該模組提供了許多功能和靈活性。如果你不熟悉日誌記錄,掌握它的最好方法是檢視教程(請參閱上方和右側的連結)。
該模組定義的基本類及其屬性和方法,將在下面各節中列出。
Logger 暴露了應用程式程式碼直接使用的介面。
Handler 將日誌記錄(由 logger 建立)傳送到適當的目標。
Filter 提供了更細粒度的功能,用於確定要輸出哪些日誌記錄。
Formatter 指定了最終輸出中日誌記錄的佈局。
Logger 物件¶
Logger 具有以下屬性和方法。請注意,Logger 永遠不應直接例項化,而應始終透過模組級函式 logging.getLogger(name)。多次使用相同的名稱呼叫 getLogger() 將始終返回對同一個 Logger 物件的引用。
name 是一個潛在的以點分隔的層級值,例如 foo.bar.baz(儘管它也可以是簡單的 foo)。在層級列表中位置更靠下的 Logger 是列表中位置更靠上的 Logger 的子級。例如,給定一個名為 foo 的 Logger,名為 foo.bar、foo.bar.baz 和 foo.bam 的 Logger 都是 foo 的後代。此外,所有 Logger 都是根 Logger 的後代。Logger 名稱層級結構類似於 Python 包層級結構,如果你使用推薦的構造 logging.getLogger(__name__) 按模組組織 Logger,則兩者是相同的。這是因為在一個模組中,__name__ 是該模組在 Python 包名稱空間中的名稱。
- class logging.Logger¶
- name¶
這是 logger 的名稱,是傳遞給
getLogger()以獲取該 logger 的值。備註
此屬性應被視為只讀。
- level¶
此 logger 的閾值,由
setLevel()方法設定。備註
不要直接設定此屬性——應始終使用
setLevel(),它會對傳遞的級別進行檢查。
- parent¶
此 logger 的父 logger。它可能會根據稍後例項化的、在名稱空間層級中位置更高的 logger 而改變。
備註
此值應被視為只讀。
- propagate¶
如果此屬性的計算結果為真,則記錄到此 logger 的事件將傳遞給更高級別(祖先)logger 的處理器,此外還會傳遞給附加到此 logger 的任何處理器。訊息會直接傳遞給祖先 logger 的處理器——不會考慮相關祖先 logger 的級別或過濾器。
如果此屬性的計算結果為假,則日誌訊息不會傳遞給祖先 logger 的處理器。
舉個例子詳細說明:如果名為
A.B.C的 logger 的 propagate 屬性的計算結果為真,任何透過logging.getLogger('A.B.C').error(...)等方法呼叫記錄到A.B.C的事件,[在透過該 logger 的級別和過濾器設定後] 將依次傳遞給附加到名為A.B、A和根 logger 的任何處理器,在此之前會先傳遞給附加到A.B.C的任何處理器。如果A.B.C、A.B、A鏈中的任何 logger 的propagate屬性設定為假,那麼該 logger 就是最後一個其處理器有機會處理該事件的 logger,傳播在該點停止。建構函式將此屬性設定為
True。備註
如果你將一個處理器附加到一個 logger *和* 它的一個或多個祖先上,它可能會多次發出相同的記錄。通常情況下,你不需要將一個處理器附加到多個 logger 上——如果你只將它附加到 logger 層級中最高的適當 logger,那麼它將看到所有後代 logger 記錄的所有事件,前提是它們的 propagate 設定保持為
True。一個常見的場景是僅將處理器附加到根 logger,讓傳播來處理其餘部分。
- handlers¶
直接附加到此 logger 例項的處理器列表。
備註
此屬性應被視為只讀;它通常透過
addHandler()和removeHandler()方法更改,這些方法使用鎖來確保執行緒安全操作。
- disabled¶
此屬性停用對任何事件的處理。它在初始化器中被設定為
False,並且只由日誌記錄配置程式碼更改。備註
此屬性應被視為只讀。
- setLevel(level)¶
將此 logger 的閾值設定為 level。嚴重性低於 level 的日誌訊息將被忽略;嚴重性為 level 或更高的日誌訊息將由此 logger 的一個或多個處理器發出,除非某個處理器的級別已設定為比 level 更高的嚴重性級別。
當一個 logger 被建立時,級別被設定為
NOTSET(當該 logger 是根 logger 時,這將導致所有訊息被處理;當該 logger 是非根 logger 時,這將導致委託給父級)。請注意,根 logger 建立時的級別為WARNING。術語“委託給父級”意味著,如果一個 logger 的級別為 NOTSET,則會遍歷其祖先 logger 鏈,直到找到一個級別不是 NOTSET 的祖先,或者到達根 logger。
如果找到了一個級別不是 NOTSET 的祖先,則該祖先的級別被視為開始祖先搜尋的 logger 的有效級別,並用於確定如何處理日誌事件。
如果到達了根 logger,並且其級別為 NOTSET,則所有訊息都將被處理。否則,根 logger 的級別將被用作有效級別。
有關級別列表,請參閱日誌級別。
在 3.2 版本發生變更: level 引數現在接受級別的字串表示形式,例如 'INFO',作為整數常量(如
INFO)的替代。但請注意,級別在內部儲存為整數,getEffectiveLevel()和isEnabledFor()等方法將返回/期望被傳遞整數。
- isEnabledFor(level)¶
指示此 logger 是否會處理嚴重性為 level 的訊息。此方法首先檢查由
logging.disable(level)設定的模組級級別,然後檢查由getEffectiveLevel()確定的 logger 的有效級別。
- getEffectiveLevel()¶
指示此 logger 的有效級別。如果已使用
setLevel()設定了非NOTSET的值,則返回該值。否則,將向根方向遍歷層級結構,直到找到一個非NOTSET的值,並返回該值。返回的值是一個整數,通常是logging.DEBUG、logging.INFO等之一。
- getChild(suffix)¶
返回一個作為此 logger 後代的 logger,由字尾確定。因此,
logging.getLogger('abc').getChild('def.ghi')將返回與logging.getLogger('abc.def.ghi')相同的 logger。這是一個方便的方法,當父 logger 使用例如__name__而不是字面量字串命名時非常有用。在 3.2 版本加入。
- getChildren()¶
返回一組作為此 logger 直接子級的 logger。例如,
logging.getLogger().getChildren()可能會返回一個包含名為foo和bar的 logger 的集合,但名為foo.bar的 logger 不會包含在該集合中。同樣,logging.getLogger('foo').getChildren()可能會返回一個包含名為foo.bar的 logger 的集合,但不會包含名為foo.bar.baz的 logger。3.12 新版功能.
- debug(msg, *args, **kwargs)¶
在此 logger 上記錄一條級別為
DEBUG的訊息。msg 是訊息格式字串,args 是使用字串格式化操作符合併到 msg 中的引數。(請注意,這意味著你可以在格式字串中使用關鍵字,並附帶一個字典引數。)如果沒有提供 args,則不會對 msg 執行 % 格式化操作。在 kwargs 中有四個被檢查的關鍵字引數:exc_info、stack_info、stacklevel 和 extra。
如果 exc_info 的計算結果不為 false,則會將異常資訊新增到日誌訊息中。如果提供了異常元組(格式與
sys.exc_info()返回的相同)或異常例項,則使用它;否則,將呼叫sys.exc_info()來獲取異常資訊。第二個可選的關鍵字引數是 stack_info,預設為
False。如果為真,則會將堆疊資訊(包括實際的日誌呼叫)新增到日誌訊息中。請注意,這與透過指定 exc_info 顯示的堆疊資訊不同:前者是從堆疊底部到當前執行緒中日誌呼叫的堆疊幀,而後者是關於在搜尋異常處理器時,因異常而展開的堆疊幀資訊。你可以獨立於 exc_info 指定 stack_info,例如,即使沒有引發異常,也可以顯示你是如何到達程式碼中某個特定點的。堆疊幀會在一個標題行後列印,該標題行顯示:
Stack (most recent call last):
這模仿了顯示異常幀時使用的
Traceback (most recent call last):。第三個可選的關鍵字引數是 stacklevel,預設為
1。如果大於 1,在計算為日誌事件建立的LogRecord中設定的行號和函式名時,將跳過相應數量的堆疊幀。這可以在日誌記錄輔助函式中使用,以便記錄的函式名、檔名和行號不是輔助函式/方法的資訊,而是其呼叫者的資訊。此引數的名稱與warnings模組中的等效引數相呼應。第四個關鍵字引數是 extra,可用於傳遞一個字典,該字典用於使用使用者定義的屬性填充為日誌事件建立的
LogRecord的__dict__。然後,你可以隨心所欲地使用這些自定義屬性。例如,可以將它們合併到日誌訊息中。例如:FORMAT = '%(asctime)s %(clientip)-15s %(user)-8s %(message)s' logging.basicConfig(format=FORMAT) d = {'clientip': '192.168.0.1', 'user': 'fbloggs'} logger = logging.getLogger('tcpserver') logger.warning('Protocol problem: %s', 'connection reset', extra=d)
會列印類似如下的內容:
2006-02-08 22:20:02,165 192.168.0.1 fbloggs Protocol problem: connection reset
在 extra 中傳遞的字典中的鍵不應與日誌系統使用的鍵衝突。(有關日誌系統使用哪些鍵的更多資訊,請參閱 LogRecord 屬性 部分。)
如果你選擇在日誌訊息中使用這些屬性,你需要小心一些。例如,在上面的例子中,
Formatter的格式字串期望LogRecord的屬性字典中有 'clientip' 和 'user'。如果缺少這些,訊息將不會被記錄,因為會發生字串格式化異常。因此,在這種情況下,你總是需要傳遞帶有這些鍵的 extra 字典。雖然這可能很煩人,但此功能旨在用於特殊情況,例如多執行緒伺服器,其中相同的程式碼在許多上下文中執行,並且出現的有趣條件取決於此上下文(如上面示例中的遠端客戶端 IP 地址和已認證的使用者名稱)。在這種情況下,很可能會使用專門的
Formatter和特定的Handler。如果沒有處理器附加到此 logger(或其任何祖先,考慮到相關的
Logger.propagate屬性),訊息將被髮送到lastResort上設定的處理器。在 3.2 版本發生變更: 添加了 stack_info 引數。
在 3.5 版本發生變更: exc_info 引數現在可以接受異常例項。
在 3.8 版本發生變更: 添加了 stacklevel 引數。
- warning(msg, *args, **kwargs)¶
在此 logger 上記錄一條級別為
WARNING的訊息。引數的解釋與debug()相同。備註
有一個已過時的方法
warn,其功能與warning完全相同。由於warn已被棄用,請不要使用它——請改用warning。
- exception(msg, *args, **kwargs)¶
在此 logger 上記錄一條級別為
ERROR的訊息。引數的解釋與debug()相同。異常資訊會新增到日誌訊息中。此方法只應在異常處理器中呼叫。
- addFilter(filter)¶
將指定的過濾器 filter 新增到此 logger。
- removeFilter(filter)¶
從此 logger 中移除指定的過濾器 filter。
- filter(record)¶
將此 logger 的過濾器應用於記錄,如果記錄需要處理,則返回
True。過濾器會依次被查詢,直到其中一個返回 false 值。如果沒有一個返回 false 值,記錄將被處理(傳遞給處理器)。如果有一個返回 false 值,則不會對該記錄進行進一步處理。
- addHandler(hdlr)¶
將指定的處理器 hdlr 新增到此 logger。
- removeHandler(hdlr)¶
從此 logger 中移除指定的處理器 hdlr。
- findCaller(stack_info=False, stacklevel=1)¶
查詢呼叫者的原始檔名和行號。以一個 4 元素元組的形式返回檔名、行號、函式名和堆疊資訊。除非 stack_info 為
True,否則堆疊資訊將作為None返回。stacklevel 引數從呼叫
debug()和其他 API 的程式碼中傳遞。如果大於 1,多出的部分將用於在確定要返回的值之前跳過堆疊幀。這在從輔助/包裝程式碼呼叫日誌記錄 API 時通常很有用,以便事件日誌中的資訊不是指向輔助/包裝程式碼,而是指向呼叫它的程式碼。
- handle(record)¶
透過將記錄傳遞給與此 logger 及其祖先相關聯的所有處理器(直到找到一個 propagate 值為 false 的 logger)來處理該記錄。此方法用於處理從套接字接收到的反序列化記錄,以及本地建立的記錄。使用
filter()應用 logger 級別的過濾。
- makeRecord(name, level, fn, lno, msg, args, exc_info, func=None, extra=None, sinfo=None)¶
這是一個工廠方法,可以在子類中重寫以建立專門的
LogRecord例項。
- hasHandlers()¶
檢查此 logger 是否配置了任何處理器。這是透過在此 logger 及其在 logger 層級中的父級中查詢處理器來完成的。如果找到處理器,則返回
True,否則返回False。當找到一個“propagate”屬性設定為 false 的 logger 時,該方法將停止向上搜尋層級結構——這將是最後一個被檢查是否存在處理器的 logger。在 3.2 版本加入。
在 3.7 版本發生變更: Logger 現在可以被序列化和反序列化。
日誌級別¶
日誌級別的數值在下表中給出。如果你想定義自己的級別,並且需要它們相對於預定義級別具有特定的值,這些數值就特別重要。如果你定義了一個具有相同數值的級別,它將覆蓋預定義的值;預定義的名稱將丟失。
級別 |
數值 |
含義/何時使用 |
|---|---|---|
|
0 |
當在 logger 上設定時,表示將諮詢祖先 logger 以確定有效級別。如果最終仍解析為 |
|
10 |
詳細資訊,通常只對試圖診斷問題的開發人員感興趣。 |
|
20 |
確認事情按預期工作。 |
|
30 |
表示發生了一些意外情況,或者在不久的將來可能會出現問題(例如“磁碟空間不足”)。軟體仍在按預期工作。 |
|
40 |
由於更嚴重的問題,軟體無法執行某些功能。 |
|
50 |
嚴重錯誤,表示程式本身可能無法繼續執行。 |
Handler 物件¶
Handler 具有以下屬性和方法。請注意,Handler 永遠不會被直接例項化;此類作為更有用的子類的基類。但是,子類中的 __init__() 方法需要呼叫 Handler.__init__()。
- class logging.Handler¶
- __init__(level=NOTSET)¶
透過設定其級別、將過濾器列表設定為空列表以及建立一個鎖(使用
createLock())來初始化Handler例項,以序列化對 I/O 機制的訪問。
- createLock()¶
初始化一個執行緒鎖,可用於序列化對可能不是執行緒安全的底層 I/O 功能的訪問。
- acquire()¶
獲取使用
createLock()建立的執行緒鎖。
- setLevel(level)¶
將此處理器的閾值設定為 level。嚴重性低於 level 的日誌訊息將被忽略。當建立處理器時,級別被設定為
NOTSET(這將導致所有訊息被處理)。有關級別列表,請參閱日誌級別。
在 3.2 版本發生變更: level 引數現在接受級別的字串表示形式,例如 'INFO',作為整數常量(如
INFO)的替代。
- addFilter(filter)¶
將指定的過濾器 filter 新增到此處理器。
- removeFilter(filter)¶
從此處理器中移除指定的過濾器 filter。
- filter(record)¶
將此處理器的過濾器應用於記錄,如果記錄需要處理,則返回
True。過濾器會依次被查詢,直到其中一個返回 false 值。如果沒有一個返回 false 值,記錄將被髮出。如果有一個返回 false 值,處理器將不會發出該記錄。
- flush()¶
確保所有日誌輸出都已重新整理。此版本不做任何事情,旨在由子類實現。
- handle(record)¶
根據可能已新增到處理器的過濾器,有條件地發出指定的日誌記錄。用 I/O 執行緒鎖的獲取/釋放來包裝記錄的實際發出過程。
- handleError(record)¶
當在
emit()呼叫期間遇到異常時,處理器應呼叫此方法。如果模組級屬性raiseExceptions為False,異常將被靜默忽略。這對於日誌記錄系統來說是大多數情況下所期望的——大多數使用者不關心日誌記錄系統中的錯誤,他們更關心應用程式錯誤。但是,如果你願意,可以用自定義處理器替換它。指定的記錄是發生異常時正在處理的記錄。(raiseExceptions的預設值為True,因為這在開發過程中更有用)。
- format(record)¶
對記錄進行格式化——如果設定了格式化器,則使用它。否則,使用模組的預設格式化器。
- emit(record)¶
執行任何必要的操作以實際記錄指定的日誌記錄。此版本旨在由子類實現,因此會引發
NotImplementedError。警告
此方法在獲取處理器級鎖之後被呼叫,鎖在此方法返回後被釋放。當你重寫此方法時,請注意,在呼叫任何可能呼叫日誌 API 其他部分(可能會進行鎖定)的程式碼時應小心,因為這可能導致死鎖。具體來說:
日誌記錄配置 API 會獲取模組級鎖,然後在配置這些處理器時獲取各個處理器級鎖。
許多日誌記錄 API 會鎖定模組級鎖。如果從此方法中呼叫此類 API,可能會導致死鎖,如果在另一個執行緒上進行了配置呼叫,因為該執行緒會嘗試在處理器級鎖*之前*獲取模組級鎖,而此執行緒則嘗試在處理器級鎖*之後*獲取模組級鎖(因為在此方法中,處理器級鎖已經被獲取)。
有關作為標準包含的處理器列表,請參閱 logging.handlers。
Formatter 物件¶
- class logging.Formatter(fmt=None, datefmt=None, style='%', validate=True, *, defaults=None)¶
負責將
LogRecord轉換為供人或外部系統解釋的輸出字串。- 引數:
fmt (str) – 用於整個日誌輸出的給定 style 的格式字串。可能的對映鍵取自
LogRecord物件的 LogRecord 屬性。如果未指定,則使用'%(message)s',這只是記錄的訊息。datefmt (str) – 用於日誌輸出日期/時間部分的格式字串。如果未指定,則使用
formatTime()中描述的預設格式。style (str) – 可以是
'%'、'{'或'$'之一,決定了格式字串將如何與其資料合併:使用 printf 風格的字串格式化 (%)、str.format()({) 或string.Template($)。這僅適用於 fmt(例如'%(message)s'對比'{message}'),不適用於傳遞給日誌記錄方法的實際日誌訊息。但是,有其他方法可以對日誌訊息使用{和$格式化。validate (bool) – 如果為
True(預設值),不正確或不匹配的 fmt 和 style 將引發ValueError;例如,logging.Formatter('%(asctime)s - %(message)s', style='{')。defaults (dict[str, Any]) – 一個包含在自定義欄位中使用的預設值的字典。例如,
logging.Formatter('%(ip)s %(message)s', defaults={"ip": None})
在 3.2 版本發生變更: 添加了 style 引數。
在 3.8 版本發生變更: 添加了 validate 引數。
在 3.10 版本發生變更: 添加了 defaults 引數。
- format(record)¶
記錄的屬性字典用作字串格式化操作的運算元。返回結果字串。在格式化字典之前,會執行一些準備步驟。記錄的 message 屬性是使用 msg % args 計算的。如果格式化字串包含
'(asctime)',則呼叫formatTime()來格式化事件時間。如果有異常資訊,則使用formatException()格式化並附加到訊息中。請注意,格式化的異常資訊快取在屬性 exc_text 中。這很有用,因為異常資訊可以被序列化並透過網路傳送,但如果你有多個自定義異常資訊格式化的Formatter子類,則應小心。在這種情況下,在一個格式化器完成其格式化後,你將需要清除快取的值(透過將 exc_text 屬性設定為None),以便下一個處理該事件的格式化器不會使用快取的值,而是重新計算它。如果堆疊資訊可用,它會附加在異常資訊之後,使用
formatStack()在必要時進行轉換。
- formatTime(record, datefmt=None)¶
想要使用格式化時間的格式化器應從
format()中呼叫此方法。此方法可以在格式化器中重寫以提供任何特定要求,但基本行為如下:如果指定了 datefmt(一個字串),則使用time.strftime()來格式化記錄的建立時間。否則,使用格式 '%Y-%m-%d %H:%M:%S,uuu',其中 uuu 部分是毫秒值,其他字母與time.strftime()文件中的相同。此格式的時間示例為2003-01-23 00:29:50,411。返回結果字串。此函式使用使用者可配置的函式將建立時間轉換為元組。預設情況下,使用
time.localtime();要為特定格式化器例項更改此設定,請將converter屬性設定為與time.localtime()或time.gmtime()簽名相同的函式。要為所有格式化器更改它,例如,如果你希望所有日誌記錄時間都以 GMT 顯示,請在Formatter類中設定converter屬性。在 3.3 版本發生變更: 以前,預設格式是硬編碼的,如本例所示:
2010-09-06 22:38:15,292,其中逗號前的部分由 strptime 格式字串('%Y-%m-%d %H:%M:%S')處理,逗號後的部分是毫秒值。由於 strptime 沒有毫秒的格式佔位符,因此使用另一個格式字串'%s,%03d'附加毫秒值——這兩個格式字串都已硬編碼到此方法中。經過更改後,這些字串被定義為類級屬性,可以在例項級別根據需要重寫。這些屬性的名稱是default_time_format(用於 strptime 格式字串)和default_msec_format(用於附加毫秒值)。在 3.9 版本發生變更:
default_msec_format可以為None。
- formatException(exc_info)¶
將指定的異常資訊(由
sys.exc_info()返回的標準異常元組)格式化為字串。此預設實現僅使用traceback.print_exception()。返回結果字串。
- formatStack(stack_info)¶
將指定的堆疊資訊(由
traceback.print_stack()返回的字串,但移除了最後一個換行符)格式化為字串。此預設實現僅返回輸入值。
- class logging.BufferingFormatter(linefmt=None)¶
一個基礎格式化器類,適合在要格式化多個記錄時進行子類化。你可以傳遞一個
Formatter例項,用於格式化每一行(對應單個記錄)。如果未指定,將使用預設格式化器(僅輸出事件訊息)作為行格式化器。- formatHeader(records)¶
為 records 列表返回一個頁首。基本實現僅返回空字串。如果你想要特定的行為,例如顯示記錄計數、標題或分隔線,則需要重寫此方法。
為 records 列表返回一個頁尾。基本實現僅返回空字串。如果你想要特定的行為,例如顯示記錄計數或分隔線,則需要重寫此方法。
- format(records)¶
為 records 列表返回格式化的文字。如果沒有記錄,基本實現僅返回空字串;否則,它返回頁首、每個用行格式化器格式化的記錄以及頁尾的串聯。
Filter 物件¶
Filters 可以被 Handlers 和 Loggers 用於比級別提供更復雜的過濾。基礎過濾器類只允許 logger 層級中某個點以下的事件。例如,用 'A.B' 初始化的過濾器將允許由 logger 'A.B'、'A.B.C'、'A.B.C.D'、'A.B.D' 等記錄的事件,但不允許 'A.BB'、'B.A.B' 等。如果用空字串初始化,則所有事件都透過。
- class logging.Filter(name='')¶
返回一個
Filter類的例項。如果指定了 name,它命名了一個 logger,該 logger 及其子級的事件將透過過濾器。如果 name 是空字串,則允許每個事件。- filter(record)¶
指定的記錄是否應被記錄?返回 false 表示否,true 表示是。過濾器可以就地修改日誌記錄,也可以返回一個完全不同的記錄例項,該例項將在事件的任何未來處理中替換原始日誌記錄。
請注意,附加到處理器的過濾器在處理器發出事件之前被查詢,而附加到 logger 的過濾器在事件被記錄時(使用 debug()、info() 等)被查詢,在將事件傳送到處理器之前。這意味著由後代 logger 生成的事件不會被 logger 的過濾器設定過濾,除非該過濾器也已應用於那些後代 logger。
你實際上不需要子類化 Filter:你可以傳遞任何具有具有相同語義的 filter 方法的例項。
在 3.2 版本發生變更: 你不需要建立專門的 Filter 類,或使用其他具有 filter 方法的類:你可以使用一個函式(或其他可呼叫物件)作為過濾器。過濾邏輯將檢查過濾器物件是否具有 filter 屬性:如果有,則假定它是一個 Filter,並呼叫其 filter() 方法。否則,假定它是一個可呼叫物件,並以記錄作為單個引數呼叫它。返回的值應符合 filter() 返回的值。
在 3.12 版本發生變更: 你現在可以從過濾器返回一個 LogRecord 例項來替換日誌記錄,而不是就地修改它。這允許附加到 Handler 的過濾器在日誌記錄被髮出之前修改它,而不會對其他處理器產生副作用。
儘管過濾器主要用於根據比級別更復雜的標準過濾記錄,但它們可以看到附加到的處理器或 logger 處理的每一條記錄:這在你想要做一些事情時很有用,比如計算特定 logger 或處理器處理了多少條記錄,或者在正在處理的 LogRecord 中新增、更改或移除屬性。顯然,更改 LogRecord 需要小心,但它確實允許將上下文資訊注入到日誌中(請參閱使用過濾器傳遞上下文資訊)。
LogRecord 物件¶
LogRecord 例項由 Logger 在每次記錄內容時自動建立,也可以透過 makeLogRecord() 手動建立(例如,從透過網路接收到的序列化事件)。
- class logging.LogRecord(name, level, pathname, lineno, msg, args, exc_info, func=None, sinfo=None)¶
包含與被記錄事件相關的所有資訊。
主要資訊在 msg 和 args 中傳遞,它們使用
msg % args組合以建立記錄的message屬性。- 引數:
name (str) – 用於記錄此
LogRecord表示的事件的 logger 名稱。請注意,即使它可能由附加到不同(祖先)logger 的處理器發出,LogRecord中的 logger 名稱將始終是此值。level (int) – 日誌事件的數字級別(例如
DEBUG為10,INFO為20等)。請注意,這會轉換為 LogRecord 的*兩個*屬性:levelno用於數值,levelname用於相應的級別名稱。pathname (str) – 進行日誌記錄呼叫的原始檔的完整字串路徑。
lineno (int) – 進行日誌記錄呼叫的原始檔中的行號。
msg (Any) – 事件描述訊息,可以是一個帶有可變資料佔位符的 % 格式字串,或任意物件(請參閱使用任意物件作為訊息)。
exc_info (tuple[type[BaseException], BaseException, types.TracebackType] | None) – 包含當前異常資訊的異常元組,如
sys.exc_info()返回的,如果沒有異常資訊可用,則為None。func (str | None) – 呼叫日誌記錄呼叫的函式或方法的名稱。
sinfo (str | None) – 一個文字字串,表示當前執行緒中從堆疊底部到日誌記錄呼叫的堆疊資訊。
- getMessage()¶
在合併所有使用者提供的引數和訊息後,返回此
LogRecord例項的訊息。如果提供給日誌記錄呼叫的訊息引數不是字串,則會呼叫str()將其轉換為字串。這允許使用使用者定義的類作為訊息,這些類的__str__方法可以返回實際要使用的格式字串。
在 3.2 版本發生變更: 透過提供一個用於建立記錄的工廠,使得
LogRecord的建立變得更具可配置性。該工廠可以使用getLogRecordFactory()和setLogRecordFactory()來設定(關於工廠的簽名,請參見此)。此功能可用於在建立
LogRecord時注入您自己的值。您可以使用以下模式old_factory = logging.getLogRecordFactory() def record_factory(*args, **kwargs): record = old_factory(*args, **kwargs) record.custom_attribute = 0xdecafbad return record logging.setLogRecordFactory(record_factory)
透過這種模式,可以連結多個工廠,只要它們不覆蓋彼此的屬性或無意中覆蓋上面列出的標準屬性,就不會出現意外情況。
LogRecord 屬性¶
LogRecord 有許多屬性,其中大部分派生自建構函式的引數。(請注意,LogRecord 建構函式引數和 LogRecord 屬性之間的名稱並不總是一一對應。)這些屬性可用於將記錄中的資料合併到格式字串中。下表按字母順序列出了屬性名稱、它們的含義以及在 %-風格格式字串中對應的佔位符。
如果您使用 {}-格式化 (str.format()),您可以在格式字串中使用 {attrname} 作為佔位符。如果您使用 $-格式化 (string.Template),請使用 ${attrname} 的形式。在這兩種情況下,當然,請將 attrname 替換為您想要使用的實際屬性名稱。
在使用 {}-格式化時,您可以透過在屬性名後放置格式化標誌來指定它們,用冒號分隔。例如:佔位符 {msecs:03.0f} 會將毫秒值 4 格式化為 004。有關可用選項的完整詳細資訊,請參閱 str.format() 文件。
屬性名稱 |
格式 |
描述 |
|---|---|---|
args |
您應該不需要自己格式化這個。 |
合併到 |
asctime |
|
建立 |
created |
|
建立 |
exc_info |
您應該不需要自己格式化這個。 |
異常元組(類似於 |
filename |
|
|
funcName |
|
包含日誌記錄呼叫的函式名稱。 |
levelname |
|
訊息的文字日誌記錄級別( |
levelno |
|
|
lineno |
|
發出日誌記錄呼叫的原始碼行號(如果可用)。 |
message |
|
記錄的訊息,計算為 |
模組 |
|
模組( |
msecs |
|
建立 |
msg |
您應該不需要自己格式化這個。 |
在原始日誌記錄呼叫中傳遞的格式字串。與 |
名稱 |
|
用於記錄呼叫的日誌記錄器的名稱。 |
pathname |
|
發出日誌記錄呼叫的原始檔的完整路徑名(如果可用)。 |
process (程序) |
|
程序ID(如果可用)。 |
processName |
|
程序名稱(如果可用)。 |
relativeCreated |
|
建立 LogRecord 的時間(以毫秒為單位),相對於載入日誌模組的時間。 |
stack_info |
您應該不需要自己格式化這個。 |
堆疊幀資訊(如果可用),從當前執行緒堆疊的底部,一直到導致建立此記錄的日誌記錄呼叫的堆疊幀。 |
thread |
|
執行緒ID(如果可用)。 |
threadName |
|
執行緒名稱(如果可用)。 |
taskName |
|
|
3.1 版後已移除: 添加了 processName。
3.12 版後已移除: 添加了 taskName。
LoggerAdapter 物件¶
LoggerAdapter 例項用於方便地將上下文資訊傳遞到日誌記錄呼叫中。有關用法示例,請參閱關於 向日志輸出新增上下文資訊 的部分。
- class logging.LoggerAdapter(logger, extra, merge_extra=False)¶
返回一個
LoggerAdapter例項,該例項使用一個底層的Logger例項、一個類字典物件(extra)和一個布林值(merge_extra)進行初始化,該布林值指示是否應將各個日誌呼叫的 extra 引數與LoggerAdapter的 extra 合併。預設行為是忽略各個日誌呼叫的 extra 引數,而只使用LoggerAdapter例項的 extra。- process(msg, kwargs)¶
修改傳遞給日誌記錄呼叫的訊息和/或關鍵字引數,以插入上下文資訊。此實現將作為 extra 傳遞給建構函式的物件,並使用鍵 ‘extra’ 將其新增到 kwargs 中。返回值是一個 (msg, kwargs) 元組,其中包含傳入引數的(可能已修改的)版本。
- manager¶
委託給底層 logger 上的
manager。
- _log¶
委託給底層 logger 上的
_log()方法。
除上述方法外,
LoggerAdapter還支援Logger的以下方法:debug()、info()、warning()、error()、exception()、critical()、log()、isEnabledFor()、getEffectiveLevel()、setLevel()和hasHandlers()。這些方法與Logger中的對應方法具有相同的簽名,因此您可以互換使用這兩種型別的例項。在 3.2 版本發生變更:
LoggerAdapter中添加了isEnabledFor()、getEffectiveLevel()、setLevel()和hasHandlers()方法。這些方法會委託給底層的日誌記錄器。在 3.6 版本發生變更: 添加了屬性
manager和方法_log(),它們委託給底層的日誌記錄器,並允許介面卡巢狀。在 3.13 版本發生變更: 增加了 merge_extra 引數。
執行緒安全¶
日誌模組旨在無需客戶端進行任何特殊工作即可實現執行緒安全。它透過使用執行緒鎖來實現這一點;有一個鎖用於序列化對模組共享資料的訪問,每個處理器也建立一個鎖來序列化對其底層 I/O 的訪問。
如果您使用 signal 模組實現非同步訊號處理程式,您可能無法在此類處理程式中使用日誌記錄。這是因為 threading 模組中的鎖實現並不總是可重入的,因此不能從此類訊號處理程式中呼叫。
模組級函式¶
除了上面描述的類之外,還有許多模組級別的函式。
- logging.getLogger(name=None)¶
返回具有指定名稱的日誌記錄器,如果名稱為
None,則返回層次結構中的根日誌記錄器。如果指定了名稱,則通常是點分隔的層級名稱,如 ‘a’、‘a.b’ 或 ‘a.b.c.d’。這些名稱的選擇完全取決於使用日誌記錄的開發人員,但建議使用__name__,除非您有特殊原因不這樣做,如 日誌記錄器物件 中所述。所有使用給定名稱對此函式的呼叫都返回相同的日誌記錄器例項。這意味著日誌記錄器例項永遠不需要在應用程式的不同部分之間傳遞。
- logging.getLoggerClass()¶
返回標準的
Logger類,或傳遞給setLoggerClass()的最後一個類。此函式可以在新的類定義中呼叫,以確保安裝自定義的Logger類不會撤銷其他程式碼已經應用的自定義設定。例如class MyLogger(logging.getLoggerClass()): # ... override behaviour here
- logging.getLogRecordFactory()¶
返回一個用於建立
LogRecord的可呼叫物件。在 3.2 版本加入: 提供了此函式以及
setLogRecordFactory(),以允許開發人員更好地控制表示日誌事件的LogRecord的構造方式。有關如何呼叫工廠的更多資訊,請參見
setLogRecordFactory()。
- logging.debug(msg, *args, **kwargs)¶
這是一個方便的函式,它在根日誌記錄器上呼叫
Logger.debug()。引數的處理方式與該方法中描述的完全相同。唯一的區別是,如果根日誌記錄器沒有處理程式,則在根日誌記錄器上呼叫
debug之前會呼叫basicConfig()。對於非常短的指令碼或
logging功能的快速演示,debug和其他模組級函式可能很方便。但是,大多數程式會希望仔細並明確地控制日誌配置,因此應優先建立模組級日誌記錄器,並在其上呼叫Logger.debug()(或其他特定級別的方法),如本文件開頭所述。
- logging.warning(msg, *args, **kwargs)¶
在根日誌記錄器上記錄一條級別為
WARNING的訊息。引數和行為與debug()相同。備註
有一個已過時的函式
warn,其功能與warning相同。由於warn已被棄用,請不要使用它 - 請改用warning。
- logging.exception(msg, *args, **kwargs)¶
在根日誌記錄器上記錄一條級別為
ERROR的訊息。引數和行為與debug()相同。異常資訊會新增到日誌訊息中。此函式只應在異常處理程式中呼叫。
- logging.disable(level=CRITICAL)¶
為所有日誌記錄器提供一個覆蓋級別 level,該級別優先於日誌記錄器自身的級別。當需要臨時限制整個應用程式的日誌輸出時,此函式非常有用。它的效果是停用所有嚴重性為 level 及以下的日誌呼叫,因此如果您使用 INFO 值呼叫它,則所有 INFO 和 DEBUG 事件都將被丟棄,而嚴重性為 WARNING 及以上的事件將根據日誌記錄器的有效級別進行處理。如果呼叫
logging.disable(logging.NOTSET),它會有效地移除此覆蓋級別,以便日誌輸出再次取決於各個日誌記錄器的有效級別。請注意,如果您定義了任何高於
CRITICAL的自定義日誌級別(不推薦這樣做),您將無法依賴 level 引數的預設值,而必須顯式提供一個合適的值。在 3.7 版本發生變更: level 引數預設設定為
CRITICAL級別。有關此更改的更多資訊,請參閱 bpo-28524。
- logging.addLevelName(level, levelName)¶
在一個內部字典中將級別 level 與文字 levelName 關聯,該字典用於將數字級別對映到文字表示,例如當
Formatter格式化訊息時。此函式也可用於定義您自己的級別。唯一的限制是,所有使用的級別都必須使用此函式註冊,級別應該是正整數,並且它們應該按嚴重性遞增的順序增加。備註
如果您正在考慮定義自己的級別,請參閱關於 自定義級別 的部分。
- logging.getLevelNamesMapping()¶
返回從級別名稱到其相應日誌級別的對映。例如,字串 "CRITICAL" 對映到
CRITICAL。每次呼叫此函式時,返回的對映都是從內部對映中複製的。在 3.11 版本中新增。
- logging.getLevelName(level)¶
返回日誌級別 level 的文字或數字表示。
如果 level 是預定義級別
CRITICAL、ERROR、WARNING、INFO或DEBUG之一,則您會得到相應的字串。如果您已使用addLevelName()將級別與名稱關聯,則返回您與 level 關聯的名稱。如果傳入與定義級別之一對應的數值,則返回相應的字串表示。level 引數也接受級別的字串表示,例如 'INFO'。在這種情況下,此函式返回該級別對應的數值。
如果沒有匹配的數字或字串值傳入,則返回字串 ‘Level %s’ % level。
備註
級別在內部是整數(因為它們需要在日誌邏輯中進行比較)。此函式用於在整數級別和透過
%(levelname)s格式說明符(參見 LogRecord 屬性)在格式化日誌輸出中顯示的級別名稱之間進行轉換,反之亦然。在 3.4 版本發生變更: 在 Python 3.4 之前的版本中,此函式也可以傳遞一個文字級別,並返回該級別的相應數值。這種未文件化的行為被認為是一個錯誤,並在 Python 3.4 中被移除,但在 3.4.2 中為了保持向後相容性而恢復。
- logging.getHandlerByName(name)¶
返回具有指定 name 的處理器,如果不存在該名稱的處理器,則返回
None。3.12 新版功能.
- logging.getHandlerNames()¶
返回一個包含所有已知處理器名稱的不可變集合。
3.12 新版功能.
- logging.makeLogRecord(attrdict)¶
建立並返回一個新的
LogRecord例項,其屬性由 attrdict 定義。此函式對於獲取一個透過套接字傳送的、經過 pickle 處理的LogRecord屬性字典,並在接收端將其重構為LogRecord例項非常有用。
- logging.basicConfig(**kwargs)¶
透過建立一個帶有預設
Formatter的StreamHandler並將其新增到根日誌記錄器,為日誌系統進行基本配置。函式debug()、info()、warning()、error()和critical()會在根日誌記錄器沒有定義處理器時自動呼叫basicConfig()。如果根日誌記錄器已經配置了處理器,此函式將不執行任何操作,除非關鍵字引數 force 設定為
True。備註
在啟動其他執行緒之前,應從主執行緒呼叫此函式。在 Python 2.7.1 和 3.2 之前的版本中,如果從多個執行緒呼叫此函式,可能會(在極少數情況下)將處理器多次新增到根日誌記錄器,導致意外結果,例如日誌中訊息重複。
支援以下關鍵字引數。
格式
描述
filename
指定建立一個
FileHandler,使用指定的檔名,而不是一個StreamHandler。filemode
如果指定了 filename,則以此 模式 開啟檔案。預設為
'a'。format
為處理器使用指定的格式字串。預設為由冒號分隔的屬性
levelname、name和message。datefmt
使用指定的日期/時間格式,該格式被
time.strftime()接受。風格
如果指定了 format,則為格式字串使用此樣式。可以是
'%'、'{'或'$'之一,分別對應 printf 風格、str.format()或string.Template。預設為'%'。level
將根日誌記錄器級別設定為指定的 級別。
stream
使用指定的流來初始化
StreamHandler。請注意,此引數與 filename 不相容——如果兩者都存在,則會引發ValueError。handlers
如果指定,這應該是一個包含已建立處理器的可迭代物件,用於新增到根日誌記錄器。任何尚未設定格式化器的處理器都將被分配此函式中建立的預設格式化器。請注意,此引數與 filename 或 stream 不相容——如果兩者都存在,則會引發
ValueError。force
如果此關鍵字引數指定為 true,則在執行其他引數指定的配置之前,將刪除並關閉附加到根日誌記錄器的任何現有處理器。
編碼
如果此關鍵字引數與 filename 一起指定,其值將在建立
FileHandler時使用,因此在開啟輸出檔案時也會使用。錯誤
如果此關鍵字引數與 filename 一起指定,其值將在建立
FileHandler時使用,因此在開啟輸出檔案時也會使用。如果未指定,則使用值 ‘backslashreplace’。請注意,如果指定了None,它將如此傳遞給open(),這意味著它將被視為與傳遞 ‘errors’ 相同。在 3.2 版本發生變更: 添加了 style 引數。
在 3.3 版本發生變更: 添加了 handlers 引數。添加了額外的檢查來捕獲指定不相容引數的情況(例如,handlers 與 stream 或 filename 一起,或者 stream 與 filename 一起)。
在 3.8 版本發生變更: 添加了 force 引數。
在 3.9 版本發生變更: 添加了 encoding 和 errors 引數。
- logging.shutdown()¶
通知日誌系統透過重新整理和關閉所有處理器來執行有序關閉。這應該在應用程式退出時呼叫,並且在此呼叫之後不應再使用日誌系統。
當匯入日誌模組時,它會將此函式註冊為退出處理器(參見
atexit),因此通常不需要手動執行此操作。
- logging.setLoggerClass(klass)¶
告訴日誌系統在例項化日誌記錄器時使用類 klass。該類應定義
__init__(),使其僅需要一個 name 引數,並且__init__()應呼叫Logger.__init__()。此函式通常在需要使用自定義日誌記錄器行為的應用程式例項化任何日誌記錄器之前呼叫。此呼叫之後,與任何其他時候一樣,不要直接使用子類例項化日誌記錄器:繼續使用logging.getLogger()API 獲取您的日誌記錄器。
- logging.setLogRecordFactory(factory)¶
設定一個用於建立
LogRecord的可呼叫物件。- 引數:
factory – 用於例項化日誌記錄的工廠可呼叫物件。
在 3.2 版本加入: 提供了此函式以及
getLogRecordFactory(),以允許開發人員更好地控制表示日誌事件的LogRecord的構造方式。工廠具有以下簽名
factory(name, level, fn, lno, msg, args, exc_info, func=None, sinfo=None, **kwargs)- name:
日誌記錄器名稱。
- level:
日誌級別(數字)。
- fn:
進行日誌記錄呼叫的檔案的完整路徑名。
- lno:
進行日誌記錄呼叫的檔案中的行號。
- msg:
日誌訊息。
- args:
日誌訊息的引數。
- exc_info:
一個異常元組,或
None。- func:
呼叫日誌記錄呼叫的函式或方法的名稱。
- sinfo:
一個堆疊回溯,例如由
traceback.print_stack()提供的,顯示呼叫層次結構。- kwargs:
額外的關鍵字引數。
模組級屬性¶
- logging.lastResort¶
透過此屬性可獲得一個“最後的處理器”。這是一個
StreamHandler,它寫入sys.stderr,級別為WARNING,用於在沒有任何日誌配置的情況下處理日誌事件。最終結果是僅將訊息列印到sys.stderr。這取代了早期的錯誤訊息,即“找不到日誌記錄器 XYZ 的處理器”。如果您出於某種原因需要早期的行為,可以將lastResort設定為None。在 3.2 版本加入。
- logging.raiseExceptions¶
用於檢視在處理期間是否應傳播異常。
預設值:
True。如果
raiseExceptions為False,則異常會被靜默忽略。這對於日誌系統來說是大多數情況下所期望的——大多數使用者不關心日誌系統中的錯誤,他們更關心應用程式錯誤。
與 warnings 模組整合¶
captureWarnings() 函式可用於將 logging 與 warnings 模組整合。
- logging.captureWarnings(capture)¶
此函式用於開啟和關閉由日誌記錄捕獲警告的功能。
如果 capture 為
True,由warnings模組發出的警告將被重定向到日誌系統。具體來說,警告將使用warnings.formatwarning()進行格式化,並將結果字串記錄到一個名為'py.warnings'的日誌記錄器中,嚴重性為WARNING。如果 capture 為
False,警告到日誌系統的重定向將停止,警告將被重定向到其原始目的地(即在呼叫captureWarnings(True)之前生效的目的地)。
參見
- 模組
logging.config 日誌模組的配置 API。
- 模組
logging.handlers 日誌模組附帶的有用的處理器。
- PEP 282 - 日誌系統
該提案描述了此功能,以便包含在 Python 標準庫中。
- 原始 Python 日誌包
這是
logging包的原始來源。此站點提供的包版本適用於 Python 1.5.2、2.1.x 和 2.2.x,這些版本在標準庫中不包含logging包。