對程序員來說,Logging 是一種非常重要的功能。無論調試程序還是程序運行時的信息顯示,Logging 都很有用。在本文中,我會演示為什么要使用以及如何使用 Python/ target=_blank class=infotextkey>Python 中的 Logging 模塊。
為什么要使用 Logging 而不使用 print()
print 語句跟 Logging 輸出存在本質上的不同。一般地,print 語句用于向 stdout(標準輸出)寫入有用的信息或程序需要輸出的信息。然而 Logging 將這些信息寫入 stderr(標準錯誤輸出)。
import logging
logging.basicConfig(level=logging.INFO) #We'll talk about this soon!
logging.warning('Something bad could hAppen!')
logging.info('You are running the program')
logging.error('Aw snap! Everything failed.')
print("This is the program output")
復制代碼
如果我運行這段程序,可以看到命令行輸出了如下信息。
$ python log_test.py
WARNING:root:Something bad could happen!INFO:root:You are running the program
ERROR:root:Aw snap! Everything failed.This is the program output
復制代碼
然而對于普通用戶來說,信息太多了。雖然這些都在命令行顯示,但數據卻被分開了。所以用戶應當這樣運行程序。
這里多說一句,小編是一名python開發工程師,這里有我自己整理了一套最新的python系統學習教程,包括從基礎的python腳本到web開發、爬蟲、數據分析、數據可視化、機器學習等。想要這些資料的可以關注小編,并私信“01”即可領取。
$ python log_test.py > program_output.txt
WARNING:root:Something bad could happen!INFO:root:You are running the program
ERROR:root:Aw snap! Everything failed.$ cat program_output.txtThis is the program output
復制代碼
在這里,需要輸出的信息通過重定向符 > 寫入到一個文件。所以我們能看到終端的運行情況,也可以方便地從文件中得到輸出信息。現在我們來了解日志等級!
Logging 和日志等級
需要使用 Logging 的原因各有不同。這些原因可以根據嚴重性的不同分為如下幾類。
- DEBUG: 開發者調試信息,包括經計算得到的值、參數估值、URL、API 調用信息等。
- INFO: 一般性的信息。
- WARNING: 關于輸入、參數等的警告。
- ERROR: 報告由于用戶操作不當或程序運行時發生的錯誤。
- CRITICAL: 最高等級的日志輸出,通常用于某些關鍵問題(取決于具體情況)。
最常用的日志類型有:DEBUG、INFO 和 ERROR。然而,經常會出現因 Python 版本不匹配拋出警告的情況。
配置 Logger 和日志處理程序
Logger 可以配置不同的參數,可以配置特定日志等級、日志文件名、文件模式和日志打印的輸出格式。
配置 Logger 的參數
Logger 可采用如下配置。
import logging
logging.basicConfig(filename='program.log', filemode='w', level=logging.DEBUG)
logging.warning('You are given a warning!')
復制代碼
上面的代碼向 program.log 文件輸出日志。filemode='w' 用于設置文件讀寫模式。filemode='w' 表示需要打開一個新文件并覆蓋原來的內容。該參數默認設置為 'a',此時會打開相應文件,并追加日志內容,因為有時需要獲取歷史日志。表示等級的參數 level 用于確定日志的最低等級。例如,當設置 level 為 INFO,程序就不會輸出 DEBUG 級別的日志。你可能知道,需要設置 'verbose=debug' 才能獲取一些參數。日志等級默認為 INFO。
創建日志處理器
雖然上述方法直接明了,滿足了一個簡單的應用程序的需求,但對于一個軟件產品或服務來說,需要全面的日志處理流程。因為很難在數以百萬計的 DEBUG 級日志中找到某個 ERROR 級日志。此外,在整個程序和模塊中,我們應當使用單一的 Logger。這樣我們就可以正確地把日志添加到同一文件中。所以我們可以使用具有不同配置的 Handler 來處理這種任務。
import logging
logger = logging.getLogger("My Logger")
logger.setLevel(logging.DEBUG)console_handler = logging.StreamHandler()file_handler = logging.FileHandler('file.log', mode='w')
console_handler.setLevel(logging.INFO)file_handler.setLevel(logging.DEBUG)logger.addHandler(console_handler)logger.addHandler(file_handler)復制代碼
可以看出,我們首先通過名稱獲取到一個 Logger。以此可以在程序的其他任意地方使用同一個 Logger。我們把全局的 Logging 等級設為最低的 DEBUG,這樣我們就可以在其他日志處理器中設置任意日志等級。
接著,我們創建兩個日志處理器,分別用于 console 和 file 形式的輸出,并設置各自的日志等級。這可以減少控制臺輸出的開銷,轉而在文件中輸出。這方便了以后的調試。
對輸出日志進行格式化
Logging 不是只用來打印我們自己的信息的。有時候我們需要打印其他信息,例如時間、日志等級、進程 ID。因此我們需要對日志進行格式化。我們來看下面的代碼。
console_format = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
file_format = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console_handler.setFormatter(console_format)file_handler.setFormatter(file_format)復制代碼
添加 Handler 之前,我們可以像上面的代碼那樣設置日志輸出的格式。可以用于設置日志格式的參數遠不止這些,你可以訪問(docs.python.org/3/library/l…
可重用的代碼
下面是我在許多應用程序中都用到的代碼段。它可能會對你有所幫助。
import logging
logger = logging.getLogger('Program Name-Version')
logger.setLevel(logging.DEBUG)formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
consoleHeader = logging.StreamHandler()consoleHeader.setFormatter(formatter)consoleHeader.setLevel(logging.INFO)fileHandler = logging.FileHandler(f"{output}/metabcc-lr.log")
fileHandler.setLevel(logging.DEBUG)fileHandler.setFormatter(formatter)logger.addHandler(fileHandler)logger.addHandler(consoleHeader)復制代碼
Logging 與多線程有關的特征
記住,Logging 模塊是線程安全的。所以除了極少數例外情況(不在本文討論范圍內)使用 Logging 不需要為多線程編寫額外的處理邏輯。
本文雖然短小、簡單,但我也希望它對初級程序員有所幫助。