logging - Logging facility for Python - Python 的日誌記錄工具

logging - Logging facility for Python - Python 的日誌記錄工具

This module defines functions and classes which implement a flexible event logging system for applications and libraries.
這個模塊爲應用與庫定義了實現靈活的事件日誌系統的函數與類。

The key benefit of having the logging API provided by a standard library module is that all Python modules can participate in logging, so your application log can include your own messages integrated with messages from third-party modules.
使用標準庫提供的 logging API 最主要的好處是,所有的 Python 模塊都可能參與日誌輸出,包括你的日誌消息和第三方模塊的日誌消息。

The module provides a lot of functionality and flexibility. If you are unfamiliar with logging, the best way to get to grips with it is to see the tutorials (see the links on the right).
這個模塊提供許多強大而靈活的功能。如果你對 logging 不太熟悉的話,掌握它最好的方式就是查看它對應的教程。

The basic classes defined by the module, together with their functions, are listed below.
該模塊定義的基礎類和函數都列在下面。

Loggers expose the interface that application code directly uses.
記錄器暴露了應用程序代碼直接使用的接口。

Handlers send the log records (created by loggers) to the appropriate destination.
處理程序將日誌記錄 (由記錄器創建) 發送到適當的目標。

Filters provide a finer grained facility for determining which log records to output.
過濾器提供了更精細的設施,用於確定要輸出的日誌記錄。

Formatters specify the layout of log records in the final output.
格式化程序指定最終輸出中日誌記錄的樣式。

facility [fə'sɪləti]:n. 設施,設備,容易,靈巧,才能,天賦
grip [grɪp]:n. 緊握,柄,支配,握拍方式,拍柄繃帶 vt. 緊握,夾緊 vi. 抓住

1. 日誌基礎教程

日誌是對軟件執行時所發生事件的一種追蹤方式。軟件開發人員對他們的代碼添加日誌調用,藉此來指示某事件的發生。一個事件通過一些包含變量數據的描述信息來描述 (每個事件發生時的數據都是不同的)。開發者還會區分事件的重要性,重要性也被稱爲等級或嚴重性。

1.1 使用日誌

對於簡單的日誌使用來說日誌功能提供了一系列便利的函數。它們是 debug()、info()、warning()、error() 和 critical()。想要決定何時使用日誌,請看下錶,其中顯示了對於每個通用任務集合來說最好的工具。

執行的任務 工具
對於命令行或程序的應用,結果顯示在控制檯。 print()
在對程序的普通操作發生時提交事件報告 (狀態監控和錯誤調查)。 logging.info() 函數 (當有診斷目的需要詳細輸出信息時使用 logging.debug() 函數)。
提出一個警告信息基於一個特殊的運行時事件。 warnings.warn() 位於代碼庫中,該事件是可以避免的,需要修改客戶端應用以消除告警。logging.warning() 不需要修改客戶端應用,但是該事件還是需要引起關注。
對一個特殊的運行時事件報告錯誤。 引發異常。
報告錯誤而不引發異常 (如在長時間運行中的服務端進程的錯誤處理)。 logging.error()、logging.exception() 或 logging.critical() 分別適用於特定的錯誤及應用領域。

日誌功能應以所追蹤事件級別或嚴重性而定。各級別適用性如下 (以嚴重性遞增):

級別 何時使用
DEBUG 細節信息,僅當診斷問題時適用。
INFO 確認程序按預期運行。
WARNING 表明有已經或即將發生的意外 (磁盤空間不足)。程序仍按預期進行。
ERROR 由於嚴重的問題,程序的某些功能已經不能正常執行。
CRITICAL 嚴重的錯誤,表明程序已不能繼續執行。

默認的級別是 WARNING,意味着只會追蹤該級別及以上的事件,除非更改日誌配置。
所追蹤事件可以以不同形式處理。最簡單的方式是輸出到控制檯。另一種常用的方式是寫入磁盤文件。

1.2 簡單的例子

import logging
logging.warning('Watch out!')  # will print a message to the console.
logging.info('I told you so')  # will not print anything.

如果你在命令行中輸入這些代碼並運行,你將會看到:

WARNING:root:Watch out!

輸出到命令行。INFO 消息並沒有出現,因爲默認級別是 WARNING。打印的信息包含事件的級別以及在日誌調用中的對於事件的描述,例如“Watch out!”。暫時不用擔心“root”部分:之後會作出解釋。輸出格式可按需要進行調整,格式化選項同樣會在之後作出解釋。

1.3 記錄日誌到文件

一種非常常見的情況是將日誌事件記錄到文件。請確認啓動新的 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())

通過 level 參數獲得你將傳遞給 basicConfig() 的值。你需要對用戶輸入數據進行錯誤排查,可如下例:

# assuming loglevel is bound to the string value obtained from the
# command line argument. Convert to upper case to allow the user to
# specify --log=DEBUG or --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)

輸出將與之前相同,但不再追加進日誌文件,因此早期運行的消息將丟失。

1.4 從多個模塊記錄日誌

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

# 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 中的模式將此概括爲多個模塊。對於這種簡單的使用模式,除了查看事件描述之外,你不能通過查看日誌文件來了解應用程序中消息的來源。

1.5 更改顯示消息的格式

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

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 已消失。爲了簡單使用,你只需要 levelname (嚴重性),message (事件描述,包括可變數據),也許在事件發生時顯示。

1.6 在消息中顯示日期/時間

要顯示事件的日期和時間,你可以在格式字符串中放置 ‘%(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() 支持的格式相同。

1.7 記錄變量數據

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

import logging
logging.warning('%s before you %s', 'Look', 'leap!')

將顯示:

WARNING:root:Look before you leap!

將可變數據合併到事件描述消息中使用舊的 %-s 形式的字符串格式化。這是爲了向後兼容:logging 包的出現時間早於較新的格式化選項例如 str.format() 和 string.Template。 這些較新格式化選項是受支持的。

2. Logging Levels - 日誌級別

The numeric values of logging levels are given in the following table. These are primarily of interest if you want to define your own levels, and need them to have specific values relative to the predefined levels. If you define a level with the same numeric value, it overwrites the predefined value; the predefined name is lost.
日誌記錄級別的數值在下表中給出。如果你想要定義自己的級別,並且需要它們具有相對於預定義級別的特定值,那麼這些內容可能是你感興趣的。如果你定義具有相同數值的級別,它將覆蓋預定義的值,預定義的名稱丟失。

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

我們自定義日誌級別時注意不要和默認的日誌級別數值相同,logging 執行時輸出大於等於設置的日誌級別的日誌信息。設置日誌級別是 INFO,則 INFO、WARNING、ERROR、CRITICAL 級別的日誌都會輸出。

如果你想要定義自己的級別,並且需要它們具有相對於預定義級別的特定值,那麼這些內容可能是你感興趣的。如果你定義具有相同數值的級別,它將覆蓋預定義的值,預定義的名稱丟失。

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

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

記錄消息通過使用 handlers 進行調度機制,它們是 Handler 類的子類的實例。處理程序負責確保記錄的消息 (以 LogRecord 的形式) 最終位於特定位置 (或一組位置),這對該消息的目標受衆 (例如最終用戶、支持服務檯員工、系統管理員、開發人員)。傳遞處理程序用於特定目標的 LogRecord 實例。每個記錄器可以有零個、一個或多個與之關聯的處理程序 (通過 Logger 的 addHandler() 方法)。除了與記錄器直接關聯的任何處理程序之外,還調用與記錄器的所有祖先相關聯的所有處理程序來分派消息 (除非記錄器的 *propagate 標誌設置爲 false 值,這將停止傳遞到上級處理程序)。

就像記錄器一樣,處理程序可以具有與它們相關聯的級別。處理程序的級別作爲過濾器,其方式與記錄器級別相同。如果處理程序決定調度一個事件,則使用 emit() 方法將消息發送到其目標。大多數用戶定義的 Handler 子類都需要重載 emit() 。

2.1 自定義級別

定義你自己的級別是可能的,但不一定是必要的,因爲現有級別是根據實踐經驗選擇的。但是,如果你確信需要自定義級別,那麼在執行此操作時應特別小心,如果你正在開發庫,則定義自定義級別可能是一個非常糟糕的主意。這是因爲如果多個庫作者都定義了他們自己的自定義級別,那麼使用開發人員很難控制和解釋這些多個庫的日誌記錄輸出,因爲給定的數值可能意味着不同的東西對於不同的庫。

3. 進階日誌教程

日誌庫採用模塊化方法,並提供幾類組件:記錄器、處理程序、過濾器和格式化程序。

  • 記錄器暴露了應用程序代碼直接使用的接口。
  • 處理程序將日誌記錄 (由記錄器創建) 發送到適當的目標。
  • 過濾器提供了更精細的附加功能,用於確定要輸出的日誌記錄。
  • 格式化程序指定最終輸出中日誌記錄的樣式。

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

通過調用 Logger 類 (以下稱爲 loggers,記錄器) 的實例來執行日誌記錄。每個實例都有一個名稱,它們在概念上以點 (句點) 作爲分隔符排列在命名空間的層次結構中。例如,名爲 ‘scan’ 的記錄器是記錄器 scan.text,scan.html 和 scan.pdf 的父級。記錄器名稱可以是你想要的任何名稱,並指示記錄消息源自的應用程序區域。

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

logger = logging.getLogger(__name__)

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

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

當然,可以將消息記錄到不同的地方。軟件包中的支持包含,用於將日誌消息寫入文件、HTTP GET/POST 位置、通過 SMTP 發送電子郵件、通用套接字、隊列或特定於操作系統的日誌記錄機制 (如 syslog 或 Windows NT 事件日誌)。目標由 handler 類提供。如果你有任何內置處理程序類未滿足的特殊要求,則可以創建自己的日誌目標類。

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

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

severity:logger name:message

你可以通過使用 format 參數將格式字符串傳遞給 basicConfig() 來更改此設置。

3.1 記錄流程

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

在這裏插入圖片描述

3.2 記錄器

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

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

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

  • Logger.setLevel() 指定記錄器將處理的最低嚴重性日誌消息,其中 debug 是最低內置嚴重性級別,critical 是最高內置嚴重性級別。例如,如果嚴重性級別爲 INFO,則記錄器將僅處理 INFO、WARNING、ERROR 和 CRITICAL 消息,並將忽略 DEBUG 消息。
  • Logger.addHandler() 和 Logger.removeHandler() 從記錄器對象中添加和刪除處理程序對象。處理程序在以下內容中有更詳細的介紹處理程序。
  • Logger.addFilter() 和 Logger.removeFilter() 可以添加或移除記錄器對象中的過濾器。Filter Objects 包含更多的過濾器細節。

你不需要始終在你創建的每個記錄器上調用這些方法。

配置記錄器對象後,以下方法將創建日誌消息:

  • 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.bar 、 foo.bar.baz 和 foo.bam 的記錄器都是 foo 子項。

記錄器具有有效等級的概念。如果未在記錄器上顯式設置級別,則使用其父級別作爲其有效級別。如果父級沒有明確的級別設置,則檢查其父級。依此類推,搜索所有上級元素,直到找到明確設置的級別。根記錄器始終具有顯式級別集 (默認情況下爲 WARNING )。在決定是否處理事件時,記錄器的有效級別用於確定事件是否傳遞給記錄器的處理程序。

子記錄器將消息傳播到與其上級記錄器關聯的處理程序。因此,不必爲應用程序使用的所有記錄器定義和配置處理程序。爲頂級記錄器配置處理程序並根據需要創建子記錄器就足夠了。(但是,你可以通過將記錄器的 propagate 屬性設置 False 來關閉傳播。)

3.3 處理程序

Handler 對象負責將適當的日誌消息 (基於日誌消息的嚴重性) 分派給處理程序的指定目標。 Logger 對象可以使用 addHandler() 方法向自己添加零個或多個處理程序對象。作爲示例場景,應用程序可能希望將所有日誌消息發送到日誌文件,將錯誤或更高的所有日誌消息發送到標準輸出,以及將所有關鍵消息發送至一個郵件地址。此方案需要三個單獨的處理程序,其中每個處理程序負責將特定嚴重性的消息發送到特定位置。

標準庫包含很多處理程序類型 (參見有用的處理程序 );教程主要使用 StreamHandler 和 FileHandler 。

處理程序中很少有方法可供應用程序開發人員使用。與使用內置處理程序對象 (即不創建自定義處理程序) 的應用程序開發人員相關的唯一處理程序方法是以下配置方法:

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

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

3.4 格式化程序

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

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

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

%Y-%m-%d %H:%M:%S

最後加上毫秒數。 style 是 {$ 之一。 如果未指定其中一個,則將使用

如果 style 是 ,則消息格式字符串使用 %(<dictionary key>)s 樣式字符串替換;可能的鍵值在 LogRecord attributes 中。如果樣式爲 {,則假定消息格式字符串與 str.format() (使用關鍵字參數)兼容,而如果樣式爲 $ ,則消息格式字符串應符合 string.Template.substitute() 。

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

'%(asctime)s - %(levelname)s - %(message)s'

格式化程序使用用戶可配置的函數將記錄的創建時間轉換爲元組。默認情況下,使用 time.localtime();要爲特定格式化程序實例更改此項,請將實例的 converter 屬性設置爲具有相同簽名的函數 time.localtime() 或 time.gmtime()。要爲所有格式化程序更改它,例如,如果你希望所有記錄時間都以 GMT 顯示,請在格式化程序類中設置 converter 屬性 (對於 GMT 顯示,設置爲 time.gmtime)。

3.5 配置日誌記錄

開發者可以通過三種方式配置日誌記錄:

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

有關最後兩個選項的參考文檔,請參閱 Configuration functions。以下示例使用 Python 代碼配置一個非常簡單的記錄器/一個控制檯處理程序和一個簡單的格式化程序:

import logging

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

# create console handler and set level to 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.warning('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.warning('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() 調用之前存在的任何非 root 記錄器被禁用。如果需要,請爲此參數指定 False。
傳遞給 dictConfig() 的字典也可以用鍵 disable_existing_loggers 指定一個布爾值,如果沒有在字典中明確指定,也默認被解釋爲 True。這會導致上面描述的記錄器禁用行爲,這可能不是你想要的 - 在這種情況下,明確地爲鍵提供 False 值。

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

在 Python 3.2 中,引入了一種新的配置日誌記錄的方法,使用字典來保存配置信息。 這提供了上述基於配置文件方法的功能的超集,並且是新應用程序和部署的推薦配置方法。因爲 Python 字典用於保存配置信息,並且由於你可以使用不同的方式填充該字典,因此你有更多的配置選項。例如,你可以使用 JSON 格式的配置文件,或者如果你有權訪問 YAML 處理功能,則可以使用 YAML 格式的文件來填充配置字典。當然,你可以在 Python 代碼中構造字典,通過套接字以 pickle 形式接收它,或者使用對你的應用程序合理的任何方法。

以下是與上述相同配置的示例,採用 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]

有關使用字典進行日誌記錄的更多信息,請參閱 Configuration functions。

如果未提供日誌記錄配置,則可能出現需要輸出日誌記錄事件但無法找到輸出事件的處理程序的情況。在這些情況下,日誌包的行爲取決於 Python 版本。

3.6 配置庫的日誌記錄

在開發使用日誌記錄的庫時,你應該注意記錄庫如何使用日誌記錄 - 例如,使用的記錄器的名稱。還需要考慮其日誌記錄配置。如果應用程序不使用日誌記錄,並且庫代碼進行日誌記錄調用,那麼嚴重性爲“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 以外的任何處理程序添加到庫的記錄器中 。這是因爲處理程序的配置是使用你的庫的應用程序開發人員的權利。應用程序開發人員瞭解他們的目標受衆以及哪些處理程序最適合他們的應用程序:如果你在底層添加處理程序,則可能會干擾他們執行單元測試和提供符合其要求的日誌的能力。

4. logging.basicConfig(**kwargs)

Does basic configuration for the logging system by creating a StreamHandler with a default Formatter and adding it to the root logger. The functions debug(), info(), warning(), error() and critical() will call basicConfig() automatically if no handlers are defined for the root logger.
通過使用默認 Formatter 創建 StreamHandler 並將其添加到根記錄器來爲日誌記錄系統執行基本配置。如果沒有爲根記錄器定義處理程序,函數 debug(),info(),warning(),error() 和 critical() 將自動調用 basicConfig()。

This function does nothing if the root logger already has handlers configured for it.
如果根記錄器已經爲其配置了處理程序,則此函數不執行任何操作。

This function should be called from the main thread before other threads are started. In versions of Python prior to 2.7.1 and 3.2, if this function is called from multiple threads, it is possible (in rare circumstances) that a handler will be added to the root logger more than once, leading to unexpected results such as messages being duplicated in the log.
在啓動其他線程之前,應該從主線程調用此函數。在 2.7.1 和 3.2 之前的 Python 版本中,如果從多個線程調用此函數,則可能 (在極少數情況下) 將處理程序多次添加到根記錄器,從而導致意外結果 (如消息) 被複制在日誌中。

The following keyword arguments are supported.
支持以下關鍵字參數。

filename: Specifies that a FileHandler be created, using the specified filename, rather than a StreamHandler.
filename:指定使用指定的文件名而不是 StreamHandler 創建 FileHandler。
指定日誌文件名。

filemode: If filename is specified, open the file in this mode. Defaults to a.
filemode:如果指定了 filename,則以此模式打開文件。默認爲 a
指定日誌文件打開模式 wa

format: Use the specified format string for the handler.
format:爲處理程序使用指定的格式字符串。

%(levelno)s:打印日誌級別的數值
%(levelname)s:打印日誌級別的名稱
%(pathname)s:打印當前執行程序的路徑,其實就是 sys.argv[0]
%(filename)s:打印當前執行程序名
%(funcName)s:打印日誌的當前函數
%(lineno)d:打印日誌的當前行號
%(asctime)s:打印日誌的時間
%(thread)d:打印線程 ID
%(threadName)s:打印線程名稱
%(process)d:打印進程 ID
%(message)s:打印日誌信息

datefmt: Use the specified date/time format, as accepted by time.strftime().
datefmt:使用 time.strftime() 接受的指定日期/時間格式。
指定時間格式。

style: If format is specified, use this style for the format string. One of %, { or $ for printf-style, str.format() or string.Template respectively. Defaults to %.
style:如果指定了 format,請將此樣式用於格式字符串。One of %, { or $ for printf-style, str.format() or string.Template respectively. Defaults to %.

level: Set the root logger level to the specified level.
level:將根記錄器級別設置爲指定級別。
設置日誌級別,默認 logging.WARNNING。

stream: Use the specified stream to initialize the StreamHandler. Note that this argument is incompatible with filename - if both are present, a ValueError is raised.
stream:使用指定的流初始化 StreamHandler。請注意,此參數與 filename 不兼容 - 如果兩者都存在,則引發 ValueError。
指定日誌的輸出流,可以指定輸出到 sys.stderr、sys.stdout 或文件,默認輸出到 sys.stderr,當 stream 和 filename 同時指定時,stream 被忽略。

handlers: If specified, this should be an iterable of already created handlers to add to the root logger. Any handlers which don’t already have a formatter set will be assigned the default formatter created in this function. Note that this argument is incompatible with filename or stream - if both are present, a ValueError is raised.
處理程序:如果指定,則應該是已創建的處理程序的可迭代,以添加到根記錄程序。任何沒有格式化程序集的處理程序都將被分配在此函數中創建的默認格式化程序。請注意,此參數與 filename 或 stream 不兼容 - 如果兩者都存在,則引發 ValueError。

4.1 基本使用方式

默認的日誌級別被設置爲 WARNING。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import logging

logging.basicConfig()

logging.debug('This is a debug message.')
logging.info('This is an info message.')
logging.warning('This is a warning message.')
logging.error('This is an error message.')
logging.critical('This is a critical message.')
/usr/bin/python2.7
WARNING:root:This is a warning message.
ERROR:root:This is an error message.
CRITICAL:root:This is a critical message.

Process finished with exit code 0

4.2 基本使用方式

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import logging

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')

logging.debug('This is a debug message.')
logging.info('This is an info message.')
logging.warning('This is a warning message.')
logging.error('This is an error message.')
logging.critical('This is a critical message.')


控制檯輸出日誌,level=logging.INFO

/usr/bin/python2.7 /home/strong/git_workspace/MonoGRNet/test.py
2019-06-23 10:34:56,857 - root - INFO - This is an info message.
2019-06-23 10:34:56,857 - root - WARNING - This is a warning message.
2019-06-23 10:34:56,857 - root - ERROR - This is an error message.
2019-06-23 10:34:56,858 - root - CRITICAL - This is a critical message.

Process finished with exit code 0

4.3 基本使用方式

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import logging

logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')

logging.debug('This is a debug message.')
logging.info('This is an info message.')
logging.warning('This is a warning message.')
logging.error('This is an error message.')
logging.critical('This is a critical message.')

控制檯輸出日誌,level=logging.DEBUG

/usr/bin/python2.7 /home/strong/git_workspace/MonoGRNet/test.py
2019-06-23 10:37:36,742 - root - DEBUG - This is a debug message.
2019-06-23 10:37:36,742 - root - INFO - This is an info message.
2019-06-23 10:37:36,742 - root - WARNING - This is a warning message.
2019-06-23 10:37:36,742 - root - ERROR - This is an error message.
2019-06-23 10:37:36,742 - root - CRITICAL - This is a critical message.

Process finished with exit code 0

5. 有用的處理程序

作爲 Handler 基類的補充,提供了很多有用的子類:

StreamHandler 實例發送消息到流 (類似文件對象)。
FileHandler 實例將消息發送到硬盤文件。
BaseRotatingHandler 是輪換日誌文件的處理程序的基類。它並不應該直接實例化。而應該使用 RotatingFileHandler 或 TimedRotatingFileHandler 代替它。
RotatingFileHandler 實例將消息發送到硬盤文件,支持最大日誌文件大小和日誌文件輪換。
TimedRotatingFileHandler 實例將消息發送到硬盤文件,以特定的時間間隔輪換日誌文件。
SocketHandler 實例將消息發送到 TCP/IP 套接字。從 3.4 開始,也支持 Unix 域套接字。
DatagramHandler 實例將消息發送到 UDP 套接字。從 3.4 開始,也支持 Unix 域套接字。
SMTPHandler 實例將消息發送到指定的電子郵件地址。
SysLogHandler 實例將消息發送到 Unix syslog 守護程序,可能在遠程計算機上。
NTEventLogHandler 實例將消息發送到 Windows NT/2000/XP 事件日誌。
MemoryHandler 實例將消息發送到內存中的緩衝區,只要滿足特定條件,緩衝區就會刷新。
HTTPHandler 實例使用 GET 或 POST 方法將消息發送到 HTTP 服務器。
WatchedFileHandler 實例會監視他們要寫入日誌的文件。如果文件發生更改,則會關閉該文件並使用文件名重新打開。此處理程序僅在類 Unix 系統上有用; Windows 不支持依賴的基礎機制。
QueueHandler 實例將消息發送到隊列,例如在 queue 或 multiprocessing 模塊中實現的隊列。
NullHandler 實例對錯誤消息不執行任何操作。它們由想要使用日誌記錄的庫開發人員使用,但是想要避免如果庫用戶沒有配置日誌記錄,則顯示 “無法找到記錄器 XXX 的消息處理器” 消息的情況。有關更多信息,請參閱 配置庫的日誌記錄 。

The NullHandler、StreamHandler 和 FileHandler 類在覈心日誌包中定義。其他處理程序定義在 logging.handlers 中。(還有另一個子模塊 logging.config,用於配置功能)

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

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

當基於記錄器級別和處理程序級別的過濾不夠時,可以將 Filter 的實例添加到 Logger 和 Handler 實例 (通過它們的 addFilter() 方法)。在決定進一步處理消息之前,記錄器和處理程序都會查詢其所有過濾器以獲得許可。如果任何過濾器返回 false 值,則不會進一步處理該消息。

基本 Filter 的功能允許按特定的記錄器名稱進行過濾。如果使用此功能,則允許通過過濾器發送到指定記錄器及其子項的消息,並丟棄所有消息。

6. 記錄日誌中引發的異常

日誌包設計爲忽略記錄日誌生產時發生的異常。這樣,處理日誌記錄事件時發生的錯誤 (例如日誌記錄錯誤配置、網絡或其他類似錯誤) 不會導致使用日誌記錄的應用程序過早終止。

SystemExit 和 KeyboardInterrupt 異常永遠不會被忽略。在 Handler 子類的 emit() 方法中發生的其他異常被傳遞給它的 handleError() 方法。

Handler 中默認實現的 handleError() 檢查是否設置了模塊級變量 raiseExceptions。如果有設置,則會將跟蹤打印到 sys.stderr。如果未設置,則忽略異常。

raiseExceptions 默認值是 True。這是因爲在開發期間,你通常希望收到任何發生異常的通知。建議你將 raiseExceptions 設置爲 False 以供生產環境使用。

7. 使用任意對象作爲消息

在前面的部分和示例中,都假設記錄事件時傳遞的消息是字符串。但是,這不是唯一的可能性。你可以將任意對象作爲消息傳遞,並且當日志記錄系統需要將其轉換爲字符串表示時,將調用其 __ str__() 方法。實際上,如果你願意,你可以完全避免計算字符串表示。例如,SocketHandler 通過 pickle 併網絡發送來發出事件。

8. 優化

消息參數的格式化將被推遲,直到無法避免。但是,計算傳遞給日誌記錄方法的參數也可能很消耗資源,如果記錄器只是丟棄你的事件,你可能希望避免這樣做。要決定做什麼,可以調用 isEnabledFor() 方法,該方法接受一個 level 參數,如果記錄器爲該級別的調用創建了該事件,則返回 true。你可以寫這樣的代碼:

if logger.isEnabledFor(logging.DEBUG):
    logger.debug('Message with %s, %s', expensive_func1(),
                                        expensive_func2())

因此,如果記錄器的閾值設置在 DEBUG 以上,則永遠不會調用 expensive_func1() 和 expensive_func2()。

在某些情況下,isEnabledFor() 本身可能比你想要的更消耗資源 (例如,對於深度嵌套的記錄器,其中顯式級別僅在記錄器層次結構中設置爲高)。在這種情況下 (或者如果你想避免在緊密循環中調用方法),你可以在本地或實例變量中將調用的結果緩存到 isEnabledFor(),並使用它而不是調用每次方法。只在日誌記錄配置在應用程序運行時動態更改 (這不常見) 時需重新計算這樣的緩存值。
對於需要對收集的日誌信息進行更精確控制的特定應用程序,還可以進行其他優化。 以下是你可以執行的操作列表,以避免在你不需要的日誌記錄期間進行處理:

有關調用來源的信息 - 如何避免收集它
將 logging._srcfile 設置爲 None 。這避免了調用 sys._getframe() ,如果 PyPy 支持 Python 3.x ,這可能有助於加速 PyPy (無法加速使用 sys._getframe() 的代碼)等環境中的代碼.

線程信息 - 如何避免收集它
將 logging.logThreads 置爲 0 。

進程信息 - 如何避免收集它
將 logging.logProcesses 置爲 0 。

核心日誌記錄模塊僅包含基本處理程序。如果你不導入 logging.handlers 和 logging.config,它們將不會佔用任何內存。

References

https://docs.python.org/zh-cn/3.7/howto/logging.html
https://docs.python.org/zh-cn/3.7/howto/logging-cookbook.html
https://docs.python.org/3.7/library/logging.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章