提供紀錄檔記錄的介面和眾多處理模組,供使用者儲存各種格式的紀錄檔,幫助偵錯程式或者記錄程式執行過程中的輸出資訊。
logging 紀錄檔等級分為五個等級,優先順序從高到低依次是 :
**CRITICAL; ** 程式嚴重錯誤
**ERROR; ** 程式錯誤/部分功能錯誤
**WARNING; ** 程式有發生錯誤的可能
**INFO; ** 程式正常執行時的資訊
DEBUG 程式偵錯資訊
預設的紀錄檔的記錄等級為 WARNING, 即當紀錄檔的等級大於獲等於 WARNING 時才會被記錄。
一般常用的記錄等級為 INFO,其用於記錄程式的正常執行的一些資訊(類似於print)。
當紀錄檔的等級達到 WARNING 以上時,表明此時程式不能正常執行;
logging.basicConfig(**kwargs)
在沒有顯式的進行建立記錄器(logger)時,會預設建立一個root logger,而logging.basicConfig(**kwargs) 可以建立帶有預設的Formatter的streamHandle並將其新增到根紀錄檔記錄器中來初始化基本設定。
比如
import logging
logging.debug('Debug code!')
logging.info('Run code!')
logging.warning('Watch out!')
logging.error('This is an error')
logging.critical('This is a ciritical')
上面程式碼中 logging 並沒有顯式的建立logger( logging.getLogger ), 其在直接使用debug(), info(), warning(), error(), critical() 時會使用預設的 root logger,並會自動呼叫 自定義的或者預設的logging.basicConfig(**kwargs) 初始化 root logger。
自定義的 logging.basicConfig(**kwargs) 中的引數 有以下的主要的選項:
引數 | 功能 |
---|---|
filename | 指定儲存紀錄檔的檔名,用指定檔名建立一個FileHandler,記錄的紀錄檔會儲存到該檔案中 |
format | 指定輸出的格式和內容,預設是以冒號分割的levalname、name 和 message |
datefmt | 使用指定的日期/時間格式,與 time.strftime() 所接受的格式相同。 |
level | 指定根紀錄檔記錄器級別,預設為 logging.WARNING |
stream | 指定紀錄檔的輸出流,可以指定輸出到sys.stderr,std.stdout 或 檔案,預設輸出到sys.stderr。使用指定的流初始化StramHandler,注意:stream和filename引數不相容,如果兩者同時使用,則會引發ValueError 錯誤 |
例如下面通過自定義 logging.basicConfig(**kwargs) 來初始化 root logger 來獲得DEBUG級別及以上的紀錄檔記錄並儲存到 log.txt 檔案中。
import logging
logging.basicConfig(filename='./log.txt',
format='%(asctime)s-%(name)s-%(levelname)s-%(message)s-%(funcName)s:%(lineno)d',
level=logging.DEBUG)
logging.debug('Debug code!')
logging.info('Run code!')
logging.warning('Watch out!')
logging.error('This is an error')
logging.critical('This is a ciritical')
Logger
除了根記錄器(root logger)外,最主要的是可以自己建立紀錄檔記錄器。
通過模組級別的函數 logging.getLogger(name)
範例化記錄器
預設情況下,記錄器採用層級結構,通過 .
來區分不同的層級。比如 有個名叫 foo
的記錄器 則 foo.a
和 foo.b
都是 foo
的子級記錄器。當然,最開始的或者說最上層的記錄器就是 root logger。如果 name=None,構建的是root logger。
可以直接用當前模組的名稱當作記錄器的名字 logging.getLogger(__name__)
子級記錄器通常不需要單獨設定紀錄檔級別以及 Handler,如果子級記錄器沒有單獨設定,則它的行為會委託給父級。比如說,記錄器foo
的級別為INFO,而foo.a
和 foo.b
都不設定紀錄檔級別。此時foo.a
和 foo.b
會遵循foo
的級別設定,即只記錄大於等於INFO級別的紀錄檔;而如果foo也沒設定的話,就會找到根記錄器root logger,root預設的級別為WARGING。
logger類的一些常用的方法
方法 | 功能描述 |
---|---|
Logger.setLevel() | 設定紀錄檔器(Logger)將會處理的紀錄檔訊息級別 |
Logger.addHandler() | 新增一個handler物件 |
Logger.removeHandler() | 移除一個handler物件 |
Logger.addFilter() | 新增一個filter物件 |
Logger.removeFilter() | 移除一個filter物件 |
Logger.debug() | 設定DEBUG級別的紀錄檔記錄 |
Logger.info() | 設定INFO級別的紀錄檔記錄 |
Logger.warning() | 設定WARNING級別的紀錄檔記錄 |
Logger.error() | 設定ERROR級別的紀錄檔記錄 |
Logger.critical() | 設定CRITICAL級別的紀錄檔記錄 |
Logger.exception() | 輸出堆疊追蹤資訊 |
Logger.log() | 設定一個自定義的level引數來建立一個紀錄檔記錄 |
logger 結合 後面要介紹的其他的三個元件可以實現以下的功能:
Handle
處理器;其可以控制記錄的紀錄檔輸出到什麼地方(標準輸出/檔案/...),同時處理器也可以新增 過濾器(filter)和格式控制器(formatter)來控制輸出的內容和輸出的格式。
其具有幾種常見的處理器:
logging.StreamHandler 標準流處理器,將訊息傳送到標準輸出流、錯誤流 --> logging.StreamHandler(sys.stdout) # sys.stdout 表示的是指向控制檯即標準輸出;當我們在 Python 中列印物件呼叫 print obj 時候,事實上是呼叫了 sys.stdout.write(obj+'\n')。
print 將你需要的內容列印到了控制檯,然後追加了一個換行符
logging.FileHandler 檔案處理器,將訊息傳送到檔案 --> logging.FileHandler(log_path)
logging.RotatingFileHandler 檔案處理器,檔案達到指定大小後,啟用新檔案儲存紀錄檔
logging.TimedRotatingFileHandler 檔案處理器,紀錄檔以特定的時間間隔輪換紀錄檔檔案
handle 類的一些常用的方法
Handler.setLevel() | 設定處理器將會處理的紀錄檔訊息的最低嚴重級別 |
Handler.setFormatter() | 為處理器設定一個格式物件 |
Handler.addFilter() | 為處理器新增一個過濾器物件 |
Handler.removeFilter() | 為處理器刪除一個過濾器物件 |
logging.StramHandler() | 將紀錄檔訊息傳送到輸出Stream,如std.out,std.err |
logging.FilterHandler() | 將紀錄檔訊息傳送到磁碟檔案,預設情況檔案大小會無線增長 |
RotationFileHandler() | 將紀錄檔訊息傳送到磁碟檔案,支援紀錄檔檔案按大小切割 |
TimeRotatingFileHandler() | 將紀錄檔訊息傳送到磁碟檔案,並支援紀錄檔檔案按時間切割 |
logging.handers.HTTPHandler() | 將紀錄檔訊息通過GET或POST的方式傳送給一個HTTP伺服器 |
logging.handlers.SMTPHandler() | 將紀錄檔訊息傳送email地址 |
Filter
filter元件用來過濾 logger 物件,一個 filter 可以直接新增到 logger物件上,也可以新增到 handler 物件上,而如果在logger和handler中都設定了filter,則紀錄檔是先通過logger的filter,再通過handler的filter。由於所有的資訊都可以經過filter,所以filter不僅可以過濾資訊,還可以增加資訊。
Filter 類的範例化物件可以通過 logging.Filter(name) 來建立,其中name 為 記錄器的名字,如果沒有建立過該名字的記錄器,就不會輸出任何紀錄檔:
filter = logging.Filter("foo.a")
基本過濾器類只允許低於指定的紀錄檔記錄器層級結構中低於特定層級的事件,例如 這個用 foo.a
初始化的過濾器,則foo.a.b
;foo.a.c
等紀錄檔記錄器記錄的紀錄檔都可以通過過濾器,而foo.c
; a.foo
等就不能通過。如果name為空字串,則所有的紀錄檔都能通過。
Filter 類 有 三個方法 :
Formatter
格式化紀錄檔的輸出;範例化:formatter = logging.Formatter(fmt=None,datefmt=None)
; 如果不指明 fmt,將預設使用 ‘%(message)s’ ,如果不指明 datefmt,將預設使用 ISO8601 日期格式。
其中 fmt 引數 有以下選項:
%(name)s | Logger的名字 |
---|---|
%(levelno)s | 數位形式的紀錄檔級別 |
%(levelname)s | 文字形式的紀錄檔級別;如果是logger.debug則它是DEBUG,如果是logger.error則它是ERROR |
%(pathname)s | 呼叫紀錄檔輸出函數的模組的完整路徑名,可能沒有 |
%(filename)s | 呼叫紀錄檔輸出函數的模組的檔名 |
%(module)s | 呼叫紀錄檔輸出函數的模組名 |
%(funcName)s | 呼叫紀錄檔輸出函數的函數名 |
%(lineno)d | 呼叫紀錄檔輸出函數的語句所在的程式碼行 |
%(created)f | 當前時間,用UNIX標準的表示時間的浮 點數表示 |
%(relativeCreated)d | 輸出紀錄檔資訊時的,自Logger建立以 來的毫秒數 |
%(asctime)s | 字串形式的當前時間。預設格式是 「2003-07-08 16:49:45,896」。逗號後面的是毫秒 |
%(thread)d | 執行緒ID。可能沒有 |
%(threadName)s | 執行緒名。可能沒有 |
%(process)d | 程序ID。可能沒有 |
%(message)s | 使用者輸出的訊息; 假如有logger.warning("NO Good"),則在%(message)s位置上是字串NO Good |
例如:
formatter = logging.Formatter('%(asctime)s %(levelname)-8s: %(message)s') # -表示右對齊 8表示取8位元
handler.formatter = formatter
datefmt 引數 有以下選項:
引數 | 含義 |
---|---|
%y | 兩位數的年份表示(00-99) |
%Y | 四位數的年份表示(000-9999) |
%m | 月份(01-12) |
%d | 月內中的一天(0-31) |
%H | 24小時制小時數(0-23) |
%I | 12小時制小時數(01-12) |
%M | 分鐘數(00=59) |
%S 秒 | (00-59) |
例子:
formatter = logging.Formatter("%(asctime)s %(levelname)s %(message)s","%Y%m%d-%H:%M:%S")
handler.formatter = formatter
conf 形式的設定
在 loguser.conf 中 寫入相關的資訊
[loggers]
keys=root,fileLogger,rotatingFileLogger
[handlers]
keys=consoleHandler,fileHandler,rotatingFileHandler
[formatters]
keys=simpleFormatter
[logger_root]
level=INFO
handlers=consoleHandler
[logger_fileLogger]
level=INFO
handlers=fileHandler
qualname=fileLogger
propagate=0
[logger_rotatingFileLogger]
level=INFO
handlers=consoleHandler,rotatingFileHandler
qualname=rotatingFileLogger
propagate=0
[handler_consoleHandler]
class=StreamHandler
level=INFO
formatter=simpleFormatter
args=(sys.stdout,)
[handler_fileHandler]
class=FileHandler
level=INFO
formatter=simpleFormatter
args=("logs/fileHandler_test.log", "a")
[handler_rotatingFileHandler]
class=handlers.RotatingFileHandler
level=WARNING
formatter=simpleFormatter
args=("logs/rotatingFileHandler.log", "a", 10*1024*1024, 50)
[formatter_simpleFormatter]
format=%(asctime)s - %(module)s - %(levelname)s -%(thread)d : %(message)s
datefmt=%Y-%m-%d %H:%M:%S
在使用logger時,直接匯入組態檔即可
from logging import config
with open('./loguser.conf', 'r', encoding='utf-8') as f:
## 載入設定
config.fileConfig(f)
## 建立同名Logger,其按照組態檔的handle,formatter,filter方法初始化
logger = logging.getLogger(name="fileLogger")
yaml 形式組態檔
在 loguser.yaml檔案 中 設定相關資訊
version: 1
disable_existing_loggers: False
# formatters設定了紀錄檔輸出時的樣式
# formatters定義了一組formatID,有不同的格式;
formatters:
brief:
format: "%(asctime)s - %(message)s"
simple:
format: "%(asctime)s - [%(name)s] - [%(levelname)s] :%(levelno)s: %(message)s"
datefmt: '%F %T'
# handlers設定了需要處理的紀錄檔資訊,logging模組的handler只有streamhandler和filehandler
handlers:
console:
class : logging.StreamHandler
formatter: brief
level : DEBUG
stream : ext://sys.stdout
info_file_handler:
class : logging.FileHandler
formatter: simple
level: ERROR
filename: ./logs/debug_test.log
error_file_handler:
class: logging.handlers.RotatingFileHandler
level: ERROR
formatter: simple
filename: ./logs/errors.log
maxBytes: 10485760 # 10MB #1024*1024*10
backupCount: 50
encoding: utf8
loggers:
#fileLogger, 就是在程式碼中通過logger = logging.getLogger("fileLogger")來獲得該型別的logger
my_testyaml:
level: DEBUG
handlers: [console, info_file_handler,error_file_handler]
# root為預設情況下的輸出設定, 當logging.getLogger("fileLoggername")裡面的fileLoggername沒有傳值的時候,
# 就是用的這個預設的root,如logging.getLogger(__name__)或logging.getLogger()
root:
level: DEBUG
handlers: [console]
同樣的可以通過匯入 yaml 檔案載入設定
with open('./loguser.yaml', 'r', encoding='utf-8') as f:
yaml_config = yaml.load(stream=f, Loader=yaml.FullLoader)
config.dictConfig(config=yaml_config)
root = logging.getLogger()
# 子記錄器的名字與組態檔中loggers欄位內的保持一致
# loggers:
# my_testyaml:
# level: DEBUG
# handlers: [console, info_file_handler,error_file_handler]
my_testyaml = logging.getLogger("my_testyaml")
看起來logging要比print複雜多了,那麼為什麼推薦在專案中使用 logging 記錄紀錄檔而不是使用print 輸出程式資訊呢。
相比與print logging 具有以下優點:
https://blog.csdn.net/weixin_41010198/article/details/89356417
https://www.cnblogs.com/chenyibai/p/10676574.html