(圖片來源)
在寫程式的時候使用 print 大法,大概是每個 programmer 都不陌生的招式,簡單又快速。但是有時候,在程式運行的過程中,會產生一些 event 或寶貴的 message 值得記錄下來做事後分析,這些就不是 print 大法可以勝任了,Python 有提供 logging 這個強大的 module 來讓大家使用,但是正因為他提供的功能很多,導致 configure logger 的時候有點複雜,讓許多新手望之卻步,在這裡簡單介紹大家如何使用 Python 的 logging module。
一、 訊息 5 大程度
二、Logging Level
三、Logging basicConfig
四、Logging fileConfig
五、個人使用方式
一、訊息 5 大程度
logging 為開發者提供了 5 種程度不同的描述來紀錄訊息,分別是 debug、info、waring、error、critical,這五種的嚴重比較程度,如下:
DEBUG < INFO < WARNING < ERROR / EXCEPTION < CRITICAL
(ps. exception 會額外紀錄當前拋出的例外訊息, 適用於 try except)
代碼範例:
import logging logging.debug('Hello debug!') logging.info('Hello info!') logging.warning('Hello warning!') logging.error('Hello error!') logging.critical('Hello critical!')
結果:
二、Logging Level
看完上面的執行結果,眼尖的你可能會有疑問,為什麼 debug、info 的訊息沒有印出來呢? 這就跟 logger 的 level 有關係了。logger 有提供 level 可以過濾掉嚴重程度沒那麼高的 message。
舉例來說,當我們把 logger 的 level 設為 logging.INFO 的時候,所有 debug() 的 message,將會被自動忽略,只有 info(), warning(), error(), critical() 才會處理。如果我們把 level 設成 logging.ERROR 的時候,所有 debug(), info(), warning() 的訊息將會被忽略。而在我們沒有設定 level 的時候,預設會是 logging.WARNING,這就是為什麼 debug、info 沒有印出來的原因。
實作範例中若手動將 logging level 設定為 INFO, 則 logging.info 的訊息就會印出來了!
代碼範例:
import logging logging.basicConfig(level=logging.INFO) logging.debug('Hello debug!') logging.info('Hello info!') logging.warning('Hello warning!') logging.error('Hello error!') logging.critical('Hello critical!')
結果:
實用情況:
在開發的時候使用 DEBUG level,等到上 production 的時候,會把 level 設成 WARNING。這樣有個好處是,不用修改任何 code 就可以把一些 debug 資訊都隱藏起來,比起用 print 大法,再一個一個找出來刪掉來要來得方便。
三、Logging basicConfig
在修改 logging level 的範例中,可以看到我有使用 basicConfig 的功能,除了 level 可以調用的參數如下:
參數 | 描述 |
filename | log 檔名 |
filemode | 寫入的方式 (w, w+, a+) |
format | 指定表示的方式 |
datefmt | 時間的表示 |
style | ormatter 使用 % or {} or $ |
level | 訊息顯示程度 |
stream | 指定輸出的 stream,與 filename 不相容,無法共用 |
handlers | 指定 handlers,與 filename & stream 不相容,無法共用 |
代碼範例:
import logging logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s %(message)s', datefmt='%Y-%m-%d %H:%M', handlers=[logging.FileHandler('my.log', 'w', 'utf-8'), ]) logging.debug('Hello debug!') logging.info('Hello info!') logging.warning('Hello warning!') logging.error('Hello error!') logging.critical('Hello critical!')
結果:
四、Logging fileConfig
使用 basicConfig 可以對 logging 進行一些設定,但此方法除了不易閱讀代碼,也會造成後續維護的不方便,而替代的方案就是 fileConfig,在第一次建置 config 檔案的時候會稍微麻煩,但會使代碼更加簡潔,也方便後續維護。
代碼範例:
import logging from logging.config import fileConfig fileConfig('logging_config.ini') logger = logging.getLogger() logger.debug('often makes a very good meal of %s', 'visiting tourists')
logging_config.ini:
[loggers] keys=root [handlers] keys=stream_handler [formatters] keys=formatter [logger_root] level=DEBUG handlers=stream_handler [handler_stream_handler] class=StreamHandler level=DEBUG formatter=formatter args=(sys.stderr,) [formatter_formatter] format=%(asctime)s %(name)s %(levelname)s %(message)s
結果:
重點說明:
1. loggers下面一定要有 root,若打別的名稱會錯誤
2. 有 keys 的設定,就表示要在檔案中另外設定相對的內容
假如設定了handlers
[handlers]
keys: console, file
那檔案就必須相對的要設定 [handler_console] 跟 [handler_file],而 keys 其實可以自訂 不一定要打console or file
3. handler 設定一定要有 args 參數,沒有值也要給個 args=() 不然會顯示錯誤
五、個人使用方式
由於個人實務上會使用到每日排程,來達到程式自動化處理事情的情況,像這種程式在背景處理的案例,最適合使用 logging 功能來記錄每次執行的訊息,也方便日後再除錯的時候有個參考依據,以下會以爬 yahoo 網站來當作範例,檔案架構如下:
logger.py:
import logging import os from datetime import datetime dir_path = 'D:/pythonProject/itkm/logger/logs/' # 設定 logs 目錄 filename = "{:%Y-%m-%d}".format(datetime.now()) + '.log' # 設定檔名 def create_logger(log_folder): # config logging.captureWarnings(True) # 捕捉 py waring message formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') my_logger = logging.getLogger('py.warnings') # 捕捉 py waring message my_logger.setLevel(logging.INFO) # 若不存在目錄則新建 if not os.path.exists(dir_path+log_folder): os.makedirs(dir_path+log_folder) # file handler fileHandler = logging.FileHandler(dir_path+log_folder+'/'+filename, 'w', 'utf-8') fileHandler.setFormatter(formatter) my_logger.addHandler(fileHandler) # console handler consoleHandler = logging.StreamHandler() consoleHandler.setLevel(logging.DEBUG) consoleHandler.setFormatter(formatter) my_logger.addHandler(consoleHandler) return my_logger
tutorial.py:
from logs.logger import create_logger import requests requests.packages.urllib3.disable_warnings() # 關閉警告訊息 def main(logger): url = "https://tw.yahoo.com/" headers = { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'} results = requests.get(url, verify=False, headers=headers) results.encoding = 'utf-8' # 修改編碼 data = results.text logger.info(data) # 將 yahoo html 記到 log file 中 if __name__ == '__main__': logger = create_logger('tutorial') # 在 logs 目錄下建立 tutorial 目錄 logger.info('Start \n') try: main(logger) except Exception as e: logger.exception("Runtime Error Message:") logger.info("Export Done! \n")
執行後的檔案架構如下:
log file 內容:
每個人、每種情況適合使用的方式的不盡相同,沒有最好的方式,只有最適合的方法,如果你也覺得我的方式不錯,歡迎拿去做使用,在使用上有任何問題也都可以盡情發問!
※注意
此篇文章是以 Python 3 來做教學
參考:
docs.python-guide.org : Logging
hhtucode : [Python] Basic Logging
留言列表