Python Logging 日誌管理教學

主要為各位介紹在Python程式運行時,如何透過logging機制來記錄運行中的狀態 與 錯誤紀錄。透過這套機制,可以更快速取方便的來進行程式架構改善 與 錯誤問題分析等。

郭耀文
10 min readNov 18, 2020

■ 前提摘要

我們在撰寫程式時,最基本的就是使用print方法來進行輸出甚至DeBug,這招最簡單又快速。但程式正式上線時,程式會產生許多事件 或 特出狀況,如:未預期錯誤 等。這類的狀態是非常值得被記錄下來的,方便日後進行問題查找 或 系統分析時用,這些事情可就不是單單print就可以勝任的工作,以下就為各位介紹python的logging機制。

■ Logging分級

Logging把等級分為六個等級,包含:NOTSET、DEBUG、INFO、WARNING、ERROR、CRITICAL。如下列表每個等級都有一個對應的數值,而Loggin只會紀錄數值大於設定值以上的訊息。例如:設定等級為WARNING,日後記錄數值就不會紀錄對應數值低於30以下的訊息。

Logging分級對應表

如在程式中想查詢每個Level的對應數值,可以用print(logging LEVEL 名稱),其輸出型態為int。

import loggingprint(logging.NOTSET)   # 輸出為:0
print(logging.DEBUG) # 輸出為:10
print(logging.INFO) # 輸出為:20
print(logging.WARNING) # 輸出為:30
print(logging.ERROR) # 輸出為:40
print(logging.CRITICAL) # 輸出為:50

也可以用對應數值來查詢是哪個等級的訊息,可使用 logging.getLevelName(level):

import loggingprint(logging.getLevelName(0))    # 輸出為:NOTSET
print(logging.getLevelName(10)) # 輸出為:DEBUG
print(logging.getLevelName(20)) # 輸出為:INFO
print(logging.getLevelName(30)) # 輸出為:WARNING
print(logging.getLevelName(40)) # 輸出為:ERROR
print(logging.getLevelName(50)) # 輸出為:CRITICAL

■ Logging輸出

介紹上述的Logging分級後,為大家說明一下Logging分級後的輸出,假設我們預設當前的Logging輸出為WARNING等級,分別輸出DEBUG、INFO、WARNING、ERROR、CRITICAL ,並查看其結果。

(此範範例僅示範輸出等級之間的關係,詳細的程式碼說明下述有清楚說明)

import logging
def main():
## ===== 定義Logging等級為WARNING ===== ##
logging.basicConfig(level=logging.WARNING)
logging.debug('Hello Debug')
logging.info('Hello info')
logging.warning('Hello WARNING')
logging.error('Hello ERROR')
logging.critical('Hello CRITICAL')
if __name__ == '__main__':
main()

由於定義輸出等級為WARNING,因此僅有大於等於WARNING等級的 WARNING、ERROR、CRITICAL會有輸出顯示,下述為輸出結果。

WARNING:root:Hello WARNING
ERROR:root:Hello ERROR
CRITICAL:root:Hello CRITICAL

■ Logging初始設定 & 自訂輸出

在初始設定Logging只需輸入:logging.basicConfig(level=logging.DEBUG),
預設的訊息輸出格式只有 levelname、name、message。

import logginglogging.basicConfig(level=logging.DEBUG)logging.info('Hello python')

其輸出結果為:

INFO:root:Hello python

如果想要讓我們的Logging有更詳細的輸出,可以使用下列表單中的其他相關的資訊,讓我們的Logging更加清楚。

其他Logging資訊

我們只要設定一個FORMAT,來定義Logging輸出格式,並對其初始設定中加入FORMAT即可,如下範例:

import logging# 定義輸出格式
FORMAT = '%(asctime)s %(filename)s %(levelname)s:%(message)s'
# Logging初始設定 + 上定義輸出格式
logging.basicConfig(level=logging.DEBUG, format=FORMAT)
logging.info('Hello python')

以下為輸出結果,輸出依序為日期(asctime)、執行檔案之檔名(filename)、Logging分級(levelname)、輸出訊息(message)。

2020–11–22 00:10:21,641 test.py INFO: Hello python

■ Logging堆疊追蹤

在logging中,也可以透過3種方法來記錄Exception的錯誤訊息。

  1. 直接輸出Exception訊息。
import loggingdef main():
logging.basicConfig(level=logging.DEBUG)
try:
llllllllogging.debug('Hello Debug') # 定義錯誤
logging.debug('Hello Debug')
logging.info('Hello info')
logging.warning('Hello WARNING')
logging.error('Hello ERROR')
logging.critical('Hello CRITICAL')
except Exception as e:
logging.error('Exception ERROR => ' + str(e))
if __name__ == '__main__':
main()

2. 在 logging.error() 加上 exc_info 參數,並將該參數設為 True,就可以紀錄 Exception。

import loggingdef main():    logging.basicConfig(level=logging.DEBUG)
try:
llllllllogging.debug('Hello Debug') # 定義錯誤
except Exception as e:
logging.error("Catch an exception.", exc_info=True)
if __name__ == '__main__':
main()

3. 若要在 logging 內紀錄 exception 訊息,可使用 logging.exception(),它會將 exception 添加至訊息中,此方法的等級為 ERROR,也就是說 logging.exception() 就等同於 logging.error(exc_info=True)

import loggingdef main():    logging.basicConfig(level=logging.DEBUG)
try:
llllllllogging.debug('Hello Debug') # 定義錯誤
except Exception as e:
logging.exception('Catch an exception.')
if __name__ == '__main__':
main()

■ 儲存 logging資訊

上述介紹都為logging輸出,但我們想將輸出的logging儲存的話,僅須在logging.basicConfig()裡面的filename 參數設定要儲存的日誌檔名,即可以將 logging 儲存起來。如未特別定義filename的參數時,預設值為 a (append 附加),表示會在原先產生的logging檔案後面添加新的logging訊息,之前的訊息不會被覆蓋,會在舊訊息之後繼續添加新訊息。若想要產生的logging檔案每次都不保留舊的訊息,可以將參數改為w (write 複寫)。

import loggingdef main():FORMAT = '%(asctime)s %(levelname)s: %(message)s'
logging.basicConfig(level=logging.DEBUG, filename='Log.log', filemode='a', format=FORMAT)
logging.debug('Hello debug')
logging.info('Hello info')
logging.warning('Hello warning')
logging.error('Hello error')
logging.critical('Hello critical')
if __name__ == '__main__':
main()

執行完後,會在該路徑下產生一個名為Log.log的檔案,產生的內容如下。

2020–11–23 22:12:24,323 DEBUG: debug message
2020–11–23 22:12:24,323 INFO: info message
2020–11–23 22:12:24,323 WARNING: warning message
2020–11–23 22:12:24,323 ERROR: error message
2020–11–23 22:12:24,323 CRITICAL: critical message

如欲修改產生檔名的位置,僅需修改filename後面的名稱即可,如:c://python_log//Log.log 等,即會在C槽的python_log資料夾下產生一個Log.log的檔案。

介紹完Python的Logging功能後,想必各位對Logging應該有初步的了解,想聊解更多細部功能的話,可以參考以下參考文獻 & 銘謝內的文章,裡面有更詳細的說明。

■ 補充說明:

如果想要讓Log可以依據時間切割,且只保留固定時間內的Log,讓儲存空間不會超載的話(可設定定時自動刪除與切割),可以參考下列文章。

--

--

郭耀文

AI project developer、Machine learning、Deep learning