日志 HOWTO?
- 作者
Vinay Sajip <vinay_sajip at red-dove dot com>
日志基礎教程?
日志是對軟件執行時所發生事件的一種追蹤方式。軟件開發人員對他們的代碼添加日志調用,借此來指示某事件的發生。一個事件通過一些包含變量數據的描述信息來描述(比如:每個事件發生時的數據都是不同的)。開發者還會區分事件的重要性,重要性也被稱為 等級 或 嚴重性
什么時候使用 Logging?
對于簡單的日志使用來說日志功能提供了一系列便利的函數。它們是 debug(),info(),warning(),error() 和 critical()。想要決定何時使用日志,請看下表,其中顯示了對于每個通用任務集合來說最好的工具。
你想要執行的任務 |
此任務最好的工具 |
|---|---|
對于命令行或程序的應用,結果顯示在控制臺。 |
|
在對程序的普通操作發生時提交事件報告(比如:狀態監控和錯誤調查) |
|
提出一個警告信息基于一個特殊的運行時事件 |
|
對一個特殊的運行時事件報告錯誤 |
引發異常 |
報告錯誤而不引發異常(如在長時間運行中的服務端進程的錯誤處理) |
|
日志功能應以所追蹤事件級別或嚴重性而定。各級別適用性如下(以嚴重性遞增):
級別 |
何時使用 |
|---|---|
|
細節信息,僅當診斷問題時適用。 |
|
確認程序按預期運行 |
|
表明有已經或即將發生的意外(例如:磁盤空間不足)。程序仍按預期進行 |
|
由于嚴重的問題,程序的某些功能已經不能正常執行 |
|
嚴重的錯誤,表明程序已不能繼續執行 |
默認的級別是``WARNING``,意味著只會追蹤該級別及以上的事件,除非更改日志配置。
所追蹤事件可以以不同形式處理。最簡單的方式是輸出到控制臺。另一種常用的方式是寫入磁盤文件。
一個簡單的例子?
一個非常簡單的例子:
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”部分:之后會作出解釋。輸出格式可按需要進行調整,格式化選項同樣會在之后作出解釋。
記錄日志到文件?
一種非常常見的情況是將日志事件記錄到文件,讓我們繼續往下看。請確認啟動新的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)
輸出將與之前相同,但不再追加進日志文件,因此早期運行的消息將丟失。
從多個模塊記錄日志?
如果你的程序包含多個模塊,這里有一個如何組織日志記錄的示例:
# 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!
如你所見,將可變數據合并到事件描述消息中使用舊的 %-s形式的字符串格式化。 這是為了向后兼容:logging 包的出現時間早于較新的格式化選項例如 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() 支持的格式相同。
后續步驟?
基本教程到此結束。 它應該足以讓你啟動并運行日志記錄。 日志包提供了更多功能,但為了充分利用它,你需要花更多的時間來閱讀以下部分。 如果你準備好了,可以拿一些你最喜歡的飲料然后繼續。
如果你的日志記錄需求很簡單,那么使用上面的示例將日志記錄合并到你自己的腳本中,如果你遇到問題或者不理解某些內容,請在 comp.lang.python Usenet 組上發布一個問題(在 https://groups.google.com/forum/#!forum/comp.lang.python ) ,你應該在短時間內得到幫助。
還不夠? 你可以繼續閱讀接下來的幾個部分,這些部分提供了比上面基本部分更高級或深入的教程。 之后,你可以看一下 日志操作手冊 。
進階日志教程?
日志庫采用模塊化方法,并提供幾類組件:記錄器、處理程序、過濾器和格式化程序。
記錄器暴露了應用程序代碼直接使用的接口。
處理程序將日志記錄(由記錄器創建)發送到適當的目標。
過濾器提供了更精細的附加功能,用于確定要輸出的日志記錄。
格式化程序指定最終輸出中日志記錄的樣式。
日志事件信息在 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() 來更改此設置。有關如何構造格式字符串的所有選項,請參閱 格式器對象 。
記錄器?
Logger 對象有三重任務。首先,它們向應用程序代碼公開了幾種方法,以便應用程序可以在運行時記錄消息。其次,記錄器對象根據嚴重性(默認過濾工具)或過濾器對象確定要處理的日志消息。第三,記錄器對象將相關的日志消息傳遞給所有感興趣的日志處理程序。
記錄器對象上使用最廣泛的方法分為兩類:配置和消息發送。
這些是最常見的配置方法:
Logger.setLevel()指定記錄器將處理的最低嚴重性日志消息,其中 debug 是最低內置嚴重性級別, critical 是最高內置嚴重性級別。 例如,如果嚴重性級別為 INFO ,則記錄器將僅處理 INFO 、 WARNING 、 ERROR 和 CRITICAL 消息,并將忽略 DEBUG 消息。Logger.addHandler()和Logger.removeHandler()從記錄器對象中添加和刪除處理程序對象。處理程序在以下內容中有更詳細的介紹 處理程序 。Logger.addFilter()和Logger.removeFilter()可以添加或移除記錄器對象中的過濾器。 Filter 對象 包含更多的過濾器細節。
你不需要始終在你創建的每個記錄器上調用這些方法。 請參閱本節的最后兩段。
配置記錄器對象后,以下方法將創建日志消息:
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 來關閉傳播。)
處理程序?
Handler 對象負責將適當的日志消息(基于日志消息的嚴重性)分派給處理程序的指定目標。 Logger 對象可以使用 addHandler() 方法向自己添加零個或多個處理程序對象。作為示例場景,應用程序可能希望將所有日志消息發送到日志文件,將錯誤或更高的所有日志消息發送到標準輸出,以及將所有關鍵消息發送至一個郵件地址。 此方案需要三個單獨的處理程序,其中每個處理程序負責將特定嚴重性的消息發送到特定位置。
標準庫包含很多處理程序類型(參見 有用的處理程序 );教程主要使用 StreamHandler 和 FileHandler 。
處理程序中很少有方法可供應用程序開發人員使用。與使用內置處理程序對象(即不創建自定義處理程序)的應用程序開發人員相關的唯一處理程序方法是以下配置方法:
setLevel()方法,就像在記錄器對象中一樣,指定將被分派到適當目標的最低嚴重性。為什么有兩個setLevel()方法?記錄器中設置的級別確定將傳遞給其處理程序的消息的嚴重性。每個處理程序中設置的級別確定處理程序將發送哪些消息。setFormatter()選擇一個該處理程序使用的 Formatter 對象。addFilter()和removeFilter()分別在處理程序上配置和取消配置過濾器對象。
應用程序代碼不應直接實例化并使用 Handler 的實例。 相反, Handler 類是一個基類,它定義了所有處理程序應該具有的接口,并建立了子類可以使用(或覆蓋)的一些默認行為。
格式化程序?
格式化程序對象配置日志消息的最終順序、結構和內容。 與 logging.Handler 類不同,應用程序代碼可以實例化格式化程序類,但如果應用程序需要特殊行為,則可能會對格式化程序進行子類化。構造函數有三個可選參數 —— 消息格式字符串、日期格式字符串和樣式指示符。
-
logging.Formatter.__init__(fmt=None, datefmt=None, style='%')?
如果沒有消息格式字符串,則默認使用原始消息。如果沒有日期格式字符串,則默認日期格式為:
%Y-%m-%d %H:%M:%S
最后加上毫秒數。 style 是 %,'{ ' 或 '$' 之一。 如果未指定其中一個,則將使用 '%'。
如果 style 是 '%',則消息格式字符串使用 %(<dictionary key>)s 樣式字符串替換;可能的鍵值在 LogRecord 屬性 中。 如果樣式為 '{',則假定消息格式字符串與 str.format() (使用關鍵字參數)兼容,而如果樣式為 '$' ,則消息格式字符串應符合 string.Template.substitute() 。
在 3.2 版更改: 添加 style 形參。
以下消息格式字符串將以人類可讀的格式記錄時間、消息的嚴重性以及消息的內容,按此順序:
'%(asctime)s - %(levelname)s - %(message)s'
格式化程序使用用戶可配置的函數將記錄的創建時間轉換為元組。 默認情況下,使用 time.localtime() ;要為特定格式化程序實例更改此項,請將實例的 converter 屬性設置為具有相同簽名的函數 time.localtime() 或 time.gmtime() 。 要為所有格式化程序更改它,例如,如果你希望所有記錄時間都以 GMT 顯示,請在格式化程序類中設置 converter 屬性(對于 GMT 顯示,設置為 time.gmtime )。
配置日志記錄?
開發者可以通過三種方式配置日志記錄:
使用調用上面列出的配置方法的 Python 代碼顯式創建記錄器、處理程序和格式化程序。
創建日志配置文件并使用
fileConfig()函數讀取它。創建配置信息字典并將其傳遞給
dictConfig()函數。
有關最后兩個選項的參考文檔,請參閱 配置函數 。 以下示例使用 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]
有關使用字典進行日志記錄的更多信息,請參閱 配置函數 。
如果沒有提供配置會發生什么?
如果未提供日志記錄配置,則可能出現需要輸出日志記錄事件但無法找到輸出事件的處理程序的情況。 在這些情況下,日志包的行為取決于 Python 版本。
對于3.2之前的Python版本,行為如下:
如果 logging.raiseExceptions 為
False(生產模式),則會以靜默方式丟棄該事件。如果 logging.raiseExceptions 為
True(開發模式),則會打印一條消息“無法找到記錄器 X.Y.Z 的處理程序”。
在 Python 3.2 及更高版本中,行為如下:
事件使用“最后的處理程序”輸出,存儲在
logging.lastResort中。 這個內部處理程序與任何記錄器都沒有關聯,它的作用類似于StreamHandler,它將事件描述消息寫入sys.stderr的當前值(因此服從任何可能的重定向影響)。 沒有對消息進行格式化——只打印裸事件描述消息。處理程序的級別設置為“警告”,因此將輸出此級別和更高級別的所有事件。
要獲得 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 以外的任何處理程序添加到庫的記錄器中 。這是因為處理程序的配置是使用你的庫的應用程序開發人員的權利。應用程序開發人員了解他們的目標受眾以及哪些處理程序最適合他們的應用程序:如果你在“底層”添加處理程序,則可能會干擾他們執行單元測試和提供符合其要求的日志的能力。
日志級別?
日志記錄級別的數值在下表中給出。如果你想要定義自己的級別,并且需要它們具有相對于預定義級別的特定值,那么這些內容可能是你感興趣的。如果你定義具有相同數值的級別,它將覆蓋預定義的值; 預定義的名稱丟失。
級別 |
數值 |
|---|---|
|
50 |
|
40 |
|
30 |
|
20 |
|
10 |
|
0 |
級別也可以與記錄器相關聯,由開發人員設置或通過加載已保存的日志記錄配置。在記錄器上調用日志記錄方法時,記錄器會將其自己的級別與與方法調用關聯的級別進行比較。如果記錄器的級別高于方法調用的級別,則實際上不會生成任何記錄消息。這是控制日志記錄輸出詳細程度的基本機制。
記錄消息被編碼為 LogRecord 類的實例。當記錄器決定實際記錄事件時,從記錄消息創建 LogRecord 實例。
記錄消息通過使用 handlers 進行調度機制,它們是 Handler 類的子類的實例。處理程序負責確保記錄的消息(以 LogRecord 的形式)最終位于特定位置(或一組位置),這對該消息的目標受眾(例如最終用戶、 支持服務臺員工、系統管理員、開發人員)。傳遞處理程序用于特定目標的 LogRecord 實例。 每個記錄器可以有零個、一個或多個與之關聯的處理程序(通過 Logger 的 addHandler() 方法)。除了與記錄器直接關聯的任何處理程序之外,還調用與記錄器的 所有祖先相關聯的所有處理程序來分派消息(除非記錄器的 *propagate 標志設置為false值,這將停止傳遞到上級處理程序)。
就像記錄器一樣,處理程序可以具有與它們相關聯的級別。處理程序的級別作為過濾器,其方式與記錄器級別相同。如果處理程序決定調度一個事件,則使用 emit() 方法將消息發送到其目標。大多數用戶定義的 Handler 子類都需要重載 emit() 。
自定義級別?
定義你自己的級別是可能的,但不一定是必要的,因為現有級別是根據實踐經驗選擇的。但是,如果你確信需要自定義級別,那么在執行此操作時應特別小心,如果你正在開發庫,則 定義自定義級別可能是一個非常糟糕的主意 。 這是因為如果多個庫作者都定義了他們自己的自定義級別,那么使用開發人員很難控制和解釋這些多個庫的日志記錄輸出,因為給定的數值可能意味著不同的東西 對于不同的庫。
有用的處理程序?
作為 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的消息處理器" 消息的情況。有關更多信息,請參閱 配置庫的日志記錄 。
3.1 新版功能: NullHandler 類。
3.2 新版功能: QueueHandler 類。
The NullHandler 、 StreamHandler 和 FileHandler 類在核心日志包中定義。其他處理程序定義在 logging.handlers 中。(還有另一個子模塊 logging.config ,用于配置功能)
記錄的消息被格式化為通過 Formatter 類的實例進行呈現。 它們使用適合與 % 運算符一起使用的格式字符串和字典進行初始化。
對于批量格式化多個消息,可以使用 BufferingFormatter 的實例。除了格式字符串(應用于批處理中的每個消息)之外,還提供了標題和尾部格式字符串。
當基于記錄器級別和處理程序級別的過濾不夠時,可以將 Filter 的實例添加到 Logger 和 Handler 實例(通過它們的 addFilter() 方法)。在決定進一步處理消息之前,記錄器和處理程序都會查詢其所有過濾器以獲得許可。如果任何過濾器返回 false 值,則不會進一步處理該消息。
基本 Filter 的功能允許按特定的記錄器名稱進行過濾。如果使用此功能,則允許通過過濾器發送到指定記錄器及其子項的消息,并丟棄所有消息。
記錄日志中引發的異常?
日志包設計為忽略記錄日志生產時發生的異常。這樣,處理日志記錄事件時發生的錯誤(例如日志記錄錯誤配置、網絡或其他類似錯誤)不會導致使用日志記錄的應用程序過早終止。
SystemExit 和 KeyboardInterrupt 異常永遠不會被忽略。 在 Handler 子類的 emit() 方法中發生的其他異常被傳遞給它的 handleError() 方法。
Handler 中默認實現的 handleError() 檢查是否設置了模塊級變量 raiseExceptions 。如果有設置,則會將跟蹤打印到 sys.stderr 。如果未設置,則忽略異常。
注解
raiseExceptions 默認值是 True。 這是因為在開發期間,你通常希望收到任何發生異常的通知。建議你將 raiseExceptions 設置為 False 以供生產環境使用。
使用任意對象作為消息?
在前面的部分和示例中,都假設記錄事件時傳遞的消息是字符串。 但是,這不是唯一的可能性。你可以將任意對象作為消息傳遞,并且當日志記錄系統需要將其轉換為字符串表示時,將調用其 __ str__() 方法。實際上,如果你愿意,你可以完全避免計算字符串表示。例如, SocketHandler 通過 pickle 并網絡發送來發出事件。
優化?
消息參數的格式化將被推遲,直到無法避免。但是,計算傳遞給日志記錄方法的參數也可能很消耗資源,如果記錄器只是丟棄你的事件,你可能希望避免這樣做。要決定做什么,可以調用 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.handlers 和 logging.config ,它們將不會占用任何內存。
參見
- 模塊
logging 日志記錄模塊的 API 參考。
- 模塊
logging.config 日志記錄模塊的配置 API 。
- 模塊
logging.handlers 日志記錄模塊附帶的有用處理程序。
