Files
Obsidian-Main/05. 資料收集/Programming/Python/logging.md

7.7 KiB
Raw Blame History

準備

import logging

logging level

level level number funtion
NOTSET 0
DEBUG 10 logging.debug()
INFO 20 logging.info()
WARNING 30 logging.warning()
ERROR 40 logging.error()
CRITICAL 50 logging.critical()
import logging

LOG_FORMAT = '%(asctime)s %(levelname)s: %(message)s'
LOG_FILENAME = 'C:\\RobotRun\\Output\\RobotRunDocUpdater.log'

logging.basicConfig(level=logging.INFO, filename=LOG_FILENAME, filemode='a', format=LOG_FORMAT)

logging.info('logging start')

Print Exception

logging 模組也提供可以紀錄完整的堆疊追蹤 (stack traces),若在 logging.error() 加上 exc_info 參數,並將該參數設為 True,就可以紀錄 Exception如下

import logging  
  
try:  
    x = 5 / 0
except:  
    logging.error("Catch an exception.", exc_info=True)

也可以使用logging.exception("Catch an exception."),效果跟logging.error("Catch an exception.", exc_info=True)一樣。

自訂 logging 輸出格式

預設的訊息輸出格式只有 levelnamenamemessage,下面是其他相關的資訊:

格式化字串 說明
%(asctime)s 日期時間, 格式為 YYYY-MM-DD HH:mm:SS,ms例如2018-12-13 17:20:30,567
%(filename)s 模組檔名
%(funcName)s 函數名稱
%(levelname)s 日誌的等級名稱
%(levelno)s 日誌的等級數值
%(lineno)d 呼叫日誌函數所在的行數
%(message)s 訊息
%(module)s 模組名稱
%(name)s logger 的名稱
%(pathname)s 檔案的完整路徑 (如果可用)
%(process)d process ID (如果可用)
%(thread)d 執行緒 ID (如果可用)
%(threradName)s 執行緒名稱

例:

FORMAT = '%(asctime)s %(levelname)s: %(message)s'
logging.basicConfig(level=logging.DEBUG, format=FORMAT)
logging.debug('debug message') --> 2018-12-13 17:40:34,604 DEBUG: debug message

儲存log

只要在 logging.basicConfig() 內的 filename 參數設定要儲存的日誌檔名,就可以將 logging 儲存:

import logging

FORMAT = '%(asctime)s %(levelname)s: %(message)s'
logging.basicConfig(level=logging.DEBUG, filename='myLog.log', filemode='w', format=FORMAT)
logging.debug('debug message')

預設 filemode 參數是設為 a,代表 append (附加) 的意思每次執行程式時Logging 會將新的訊息加在舊的訊息後面,不會覆蓋舊的訊息。若要改成新訊息覆蓋就訊息,那可以將 filemode 參數設為 w,代表 write 的意思。

儲存log也輸出到console

logging有4個主要module

  • Logger暴露了應用程式程式碼能直接使用的介面。
  • Handler記錄器產生的日誌記錄傳送至合適的目的地。
  • Filter提供了更好的粒度控制它可以決定輸出哪些日誌記錄。
  • Formatter指明瞭最終輸出中日誌記錄的佈局。

Handler

其中Handlers有以下幾類:

  1. logging.StreamHandler -> 控制檯輸出 使用這個Handler可以向類似與sys.stdout或者sys.stderr的任何檔案物件(file object)輸出資訊。 它的建構函式是: StreamHandler([strm]) 其中strm引數是一個檔案物件。預設是sys.stderr
  2. logging.FileHandler -> 檔案輸出 和StreamHandler類似用於向一個檔案輸出日誌資訊。不過FileHandler會幫你開啟這個檔案。 它的建構函式是:FileHandler(filename[,mode]) filename是檔名必須指定一個檔名。 mode是檔案的開啟方式。預設是'a',即新增到檔案末端。
  3. logging.handlers.RotatingFileHandler -> 按照大小自動分割日誌檔案,一旦達到指定的大小重新生成檔案 這個Handler類似於上面的FileHandler但是它可以管理檔案大小。當檔案達到一定大小之後它會自動將當前日誌檔案改名然後建立一個新的同名日誌檔案繼續輸出。比如日誌檔案是chat.log。當chat.log達到指定的大小之後RotatingFileHandler自動把 檔案改名為chat.log.1。不過如果chat.log.1已經存在會先把chat.log.1重新命名為chat.log.2。 最後重新建立 chat.log繼續輸出日誌資訊。它的建構函式是RotatingFileHandler(filename[, mode[, maxBytes[, backupCount]]]),其中filenamemode兩個引數和FileHandler一樣。maxBytes用於指定日誌檔案的最大檔案大小。如果maxBytes為0意味著日誌檔案可以無限大這時上面描述的重新命名過程就不會發生。 backupCount用於指定保留的備份檔案的個數。比如如果指定為2當上面描述的重新命名過程發生時原有的chat.log.2並不會被更名,而是被刪除。
  4. logging.handlers.TimedRotatingFileHandler -> 按照時間自動分割日誌檔案 這個Handler和RotatingFileHandler類似,不過,它沒有通過判斷檔案大小來決定何時重新建立日誌檔案,而是間隔一定時間就自動建立新的日誌檔案。重新命名的過程與RotatingFileHandler類似,不過新的檔案不是附加數字,而是當前時間。它的建構函式是:TimedRotatingFileHandler( filename [,when [,interval [,backupCount]]]),其中filename引數和backupCount引數和RotatingFileHandler具有相同的意義。interval是時間間隔。 when引數是一個字串。表示時間間隔的單位,不區分大小寫。它有以下取值: S 秒 M 分 H 小時 D 天 W 每星期(interval==0時代表星期一) midnight 每天凌晨。

Formatters

Formatters預設的時間格式為%Y-%m-%d %H:%M:%S

Example

新增2個handler一個輸出到螢幕上一個寫到檔案裡。寫到檔案裡的那個handler必須是logging.handlers.RotatingFileHandler超過1MB時會自動分割。

import logging
import logging.handlers

logger = logging.getLogger(filename)  # filename就是你要存log的檔名

shell_print = logging.StreamHandler() # 往螢幕上輸出
shell_print.setFormatter(format_str)  # 設定螢幕上顯示的格式

file_print = logging.handlers.RotatingFileHandler(
    filename=filename,
    mode='a',
    maxBytes=1024*1024,
    backupCount=backCount,
    encoding='utf-8')
file_print.setFormatter(format_str)   # 設定檔案裡寫入的格式

logger.addHandler(sh) # 把物件加到logger裡
logger.addHandler(th)

參考: