22
loading...
This website collects cookies to deliver better user experience
print()
大法不同的是,log 除了把訊息顯示在螢幕外,還有有更豐富的設定選項,例如輸出到檔案,以及設定檔案的保留週期等等,這些都是 print()
大法難以實現的特性,因此對於記錄程式行為的需求,建議還是老老實實地用專門的 log 套件來實現。logging
。import logging
logging.debug('debug message')
logging.info('info message')
logging.warning('warning message')
logging.error('error message')
logging.critical('critical message')
logging
後可以直接調用,直接調用會把訊息顯示在螢幕上,上面我們調用了五個函式,他們代表的是訊息的重大程度,在 Python 文件內的定義如下:WARNING:root:warning message
ERROR:root:error message
CRITICAL:root:critical message
logging
預設只輸出 warning 及比 warning 更嚴重(即級別 ≥ 30)的訊息,這樣的預設配置較符合正式環境的需求,避免過多無謂的 info、debug 訊息輸出的 IO 作業把效能拖垮,或把空間灌爆。import logging
dev_logger: logging.Logger = logging.getLogger(name='dev')
dev_logger.setLevel(logging.DEBUG)
handler: logging.StreamHandler = logging.StreamHandler()
dev_logger.addHandler(handler)
dev_logger.debug('debug message')
dev_logger.info('info message')
dev_logger.warning('warning message')
dev_logger.error('error message')
dev_logger.critical('critical message')
logging
的其它概念,下面一一說明。logging
我們操作的是一個稱為 root
的 logger,而現在這個範例我們用 getLogger()
函式建立了另一個 dev_logger
,後面的設定也都是基於這個新的 dev_logger
,我們可以利用 logger 的機制建立無數個 logger,每個 logger 都可以有自己的組態,例如儲存到不同的檔案,或者不同的輸出的格式等等,雖然 root
logger 也是可以被做設定的,但個人建議另外建立自己的 logger 再把它變成自己喜歡的形狀,而不要去更動 root
logger 的行為,避免無意中改掉專案內依賴套件的 logger 行為。setLevel(logging.DEBUG)
自然就是設定 logger 輸出級別的函式,裡面的 logging.DEBUG
也很單純,就是整數 10
,意即輸出 debug 及比 debug 更嚴重(即級別 ≥ 10)的的訊息。logging
模組的 handler 負責處理 log 訊息的輸出工作,例如輸出到螢幕上或者某個檔案內,而我們這邊使用了 logging.StreamHandler()
這個 logging
模組內預帶的 handler。StreamHandler()
在不加任何參數的情況下,會把訊息輸出到 stderr,在作業系統未做額外的配置下,stderr 可以簡單的理解為顯示到螢幕上。addHandler()
就望文生義了。dev_logger
除了加入一個 StreamHandler
外,還可以再定義其他的 handler,例如一個 FileHandler
,如此一則 log 訊息就會被顯示在螢幕上以及被存到某個 log 檔內。debug message
info message
warning message
error message
critical message
dev_logger
設定它的訊息格式,而原始的 root
logger 是有預設它的訊息格式的。logging
設定 log 訊息格式的特性 formatter。import logging
dev_logger: logging.Logger = logging.getLogger(name='dev')
dev_logger.setLevel(logging.DEBUG)
handler: logging.StreamHandler = logging.StreamHandler()
formatter: logging.Formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
dev_logger.addHandler(handler)
dev_logger.debug('debug message')
dev_logger.info('info message')
dev_logger.warning('warning message')
dev_logger.error('error message')
dev_logger.critical('critical message')
2005-03-19 15:10:26,618 - dev - DEBUG - debug message
2005-03-19 15:10:26,620 - dev - INFO - info message
2005-03-19 15:10:26,695 - dev - WARNING - warn message
2005-03-19 15:10:26,697 - dev - ERROR - error message
2005-03-19 15:10:26,773 - dev - CRITICAL - critical message
formatter
定義的,它是一個 logging.Formatter
物件,在實例化時餵入的字串定義了這個 formatter 的格式。除了範例內的代號,完整的格式代號如下:格式 | 描述 |
---|---|
%(asctime)s |
建立 log 時間 |
%(created)f |
建立 log 的 Unix 時間 |
%(filename)s |
發出 log 的程式檔名 |
%(funcName)s |
發出 log 的函式名 |
%(levelname)s |
log 的級別 |
%(levelno)s |
log 的級別數字 |
%(lineno)d |
發出 log 的行號 |
%(message)s |
log 的訊息內容 |
%(module)s |
發出 log 的模組名稱 |
%(msecs)d |
建立 log 時間的毫秒部份 |
%(name)s |
發出 log 的 logger 名稱 |
%(pathname)s |
發出 log 的程式的路徑 |
%(process)d |
發出 log 的程序的 ID |
%(processName)s |
發出 log 的程序名稱 |
%(relativeCreated)d |
log 從創建到發出的毫秒時間差 |
%(thread)d |
發出 log 的線程 ID |
%(threadName)s |
發出 log 的線程名稱 |
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
就是個頗不錯的格式。logging
本身的用法,若要在專案內導入 log 機制,還需要考慮這幾點:INFO: Started server process [20870]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
logging
模組的基礎用法,有些特性是本文並未涉及的,特別是組態檔的部份,logging
可以從外部的組態檔讀入配置,這樣的特性讓我們可以更好的把專案內的 log 設定與程式邏輯分離,並把所有的 logger 組織在一份獨立的檔案內,也更符合關注點分離的原則,或許未來可以再寫一篇來騙稿費。:p