Python?內(nèi)置logging?使用詳細介紹
logging 的主要作用
提供日志記錄的接口和眾多處理模塊,供用戶存儲各種格式的日志,幫助調(diào)試程序或者記錄程序運行過程中的輸出信息。
logging 日志等級
logging 日志等級分為五個等級,優(yōu)先級從高到低依次是 :
**CRITICAL; ** 程序嚴(yán)重錯誤
**ERROR; **程序錯誤/部分功能錯誤
**WARNING; **程序有發(fā)生錯誤的可能
**INFO; **程序正常運行時的信息
DEBUG程序調(diào)試信息
默認(rèn)的日志的記錄等級為 WARNING, 即當(dāng)日志的等級大于獲等于 WARNING 時才會被記錄。
一般常用的記錄等級為 INFO,其用于記錄程序的正常運行的一些信息(類似于print)。
當(dāng)日志的等級達到 WARNING 以上時,表明此時程序不能正常運行;
logging 的基礎(chǔ)函數(shù)
logging.basicConfig(**kwargs)
在沒有顯式的進行創(chuàng)建記錄器(logger)時,會默認(rèn)創(chuàng)建一個root logger,而logging.basicConfig(**kwargs) 可以創(chuàng)建帶有默認(rèn)的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 并沒有顯式的創(chuàng)建logger( logging.getLogger ), 其在直接使用debug(), info(), warning(), error(), critical() 時會使用默認(rèn)的 root logger,并會自動調(diào)用 自定義的或者默認(rèn)的logging.basicConfig(**kwargs) 初始化 root logger。
自定義的 logging.basicConfig(**kwargs) 中的參數(shù) 有以下的主要的選項:
參數(shù) | 功能 |
---|---|
filename | 指定保存日志的文件名,用指定文件名創(chuàng)建一個FileHandler,記錄的日志會保存到該文件中 |
format | 指定輸出的格式和內(nèi)容,默認(rèn)是以冒號分割的levalname、name 和 message |
datefmt | 使用指定的日期/時間格式,與 time.strftime() 所接受的格式相同。 |
level | 指定根日志記錄器級別,默認(rèn)為 logging.WARNING |
stream | 指定日志的輸出流,可以指定輸出到sys.stderr,std.stdout 或 文件,默認(rèn)輸出到sys.stderr。使用指定的流初始化StramHandler,注意:stream和filename參數(shù)不兼容,如果兩者同時使用,則會引發(fā)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')
logging 的四大組件(類)
Logger
除了根記錄器(root logger)外,最主要的是可以自己創(chuàng)建日志記錄器。
通過模塊級別的函數(shù) logging.getLogger(name)
實例化記錄器
默認(rèn)情況下,記錄器采用層級結(jié)構(gòu),通過 .
來區(qū)分不同的層級。比如 有個名叫 foo
的記錄器 則 foo.a
和 foo.b
都是 foo
的子級記錄器。當(dāng)然,最開始的或者說最上層的記錄器就是 root logger。如果 name=None,構(gòu)建的是root logger。
可以直接用當(dāng)前模塊的名稱當(dāng)作記錄器的名字 logging.getLogger(__name__)
子級記錄器通常不需要單獨設(shè)置日志級別以及 Handler,如果子級記錄器沒有單獨設(shè)置,則它的行為會委托給父級。比如說,記錄器foo
的級別為INFO,而foo.a
和 foo.b
都不設(shè)置日志級別。此時foo.a
和 foo.b
會遵循foo
的級別設(shè)置,即只記錄大于等于INFO級別的日志;而如果foo也沒設(shè)置的話,就會找到根記錄器root logger,root默認(rèn)的級別為WARGING。
logger類的一些常用的方法
方法 | 功能描述 |
---|---|
Logger.setLevel() | 設(shè)置日志器(Logger)將會處理的日志消息級別 |
Logger.addHandler() | 添加一個handler對象 |
Logger.removeHandler() | 移除一個handler對象 |
Logger.addFilter() | 添加一個filter對象 |
Logger.removeFilter() | 移除一個filter對象 |
Logger.debug() | 設(shè)置DEBUG級別的日志記錄 |
Logger.info() | 設(shè)置INFO級別的日志記錄 |
Logger.warning() | 設(shè)置WARNING級別的日志記錄 |
Logger.error() | 設(shè)置ERROR級別的日志記錄 |
Logger.critical() | 設(shè)置CRITICAL級別的日志記錄 |
Logger.exception() | 輸出堆棧追蹤信息 |
Logger.log() | 設(shè)置一個自定義的level參數(shù)來創(chuàng)建一個日志記錄 |
logger 結(jié)合 后面要介紹的其他的三個組件可以實現(xiàn)以下的功能:
- Logger需要通過handler將日志信息輸出到目標(biāo)位置,目標(biāo)位置可以是sys.stdout和文件等(這與logging.basicConfig(**kwargs) 設(shè)置中不太一致)。
- 一個Logger可以設(shè)置不同的Handler,而不同的Handler可以將日志輸出到不同的位置(不同的日志文件),并且每個Handler都可以設(shè)置自己的filter從而實現(xiàn)日志過濾,保留實際項目中需要的日志。同時每個Handler也可以設(shè)置不同的Formatter,在每個Formatter實現(xiàn)同一條日志以不同的格式輸出到不同的地方。
Handle
處理器;其可以控制記錄的日志輸出到什么地方(標(biāo)準(zhǔn)輸出/文件/...),同時處理器也可以添加 過濾器(filter)和格式控制器(formatter)來控制輸出的內(nèi)容和輸出的格式。
其具有幾種常見的處理器:
- logging.StreamHandler 標(biāo)準(zhǔn)流處理器,將消息發(fā)送到標(biāo)準(zhǔn)輸出流、錯誤流 --> logging.StreamHandler(sys.stdout) # sys.stdout 表示的是指向控制臺即標(biāo)準(zhǔn)輸出;當(dāng)我們在 Python 中打印對象調(diào)用 print obj 時候,事實上是調(diào)用了 sys.stdout.write(obj+'\n')。
- print 將你需要的內(nèi)容打印到了控制臺,然后追加了一個換行符
- logging.FileHandler 文件處理器,將消息發(fā)送到文件 --> logging.FileHandler(log_path)
- logging.RotatingFileHandler 文件處理器,文件達到指定大小后,啟用新文件存儲日志
- logging.TimedRotatingFileHandler 文件處理器,日志以特定的時間間隔輪換日志文件
handle 類的一些常用的方法
Handler.setLevel() | 設(shè)置處理器將會處理的日志消息的最低嚴(yán)重級別 |
Handler.setFormatter() | 為處理器設(shè)置一個格式對象 |
Handler.addFilter() | 為處理器添加一個過濾器對象 |
Handler.removeFilter() | 為處理器刪除一個過濾器對象 |
logging.StramHandler() | 將日志消息發(fā)送到輸出Stream,如std.out,std.err |
logging.FilterHandler() | 將日志消息發(fā)送到磁盤文件,默認(rèn)情況文件大小會無線增長 |
RotationFileHandler() | 將日志消息發(fā)送到磁盤文件,支持日志文件按大小切割 |
TimeRotatingFileHandler() | 將日志消息發(fā)送到磁盤文件,并支持日志文件按時間切割 |
logging.handers.HTTPHandler() | 將日志消息通過GET或POST的方式發(fā)送給一個HTTP服務(wù)器 |
logging.handlers.SMTPHandler() | 將日志消息發(fā)送email地址 |
Filter
filter組件用來過濾 logger 對象,一個 filter 可以直接添加到 logger對象上,也可以添加到 handler 對象上,而如果在logger和handler中都設(shè)置了filter,則日志是先通過logger的filter,再通過handler的filter。由于所有的信息都可以經(jīng)過filter,所以filter不僅可以過濾信息,還可以增加信息。
Filter 類的實例化對象可以通過 logging.Filter(name) 來創(chuàng)建,其中name 為 記錄器的名字,如果沒有創(chuàng)建過該名字的記錄器,就不會輸出任何日志:
filter = logging.Filter("foo.a")
基本過濾器類只允許低于指定的日志記錄器層級結(jié)構(gòu)中低于特定層級的事件,例如 這個用 foo.a
初始化的過濾器,則foo.a.b
;foo.a.c
等日志記錄器記錄的日志都可以通過過濾器,而foo.c
; a.foo
等就不能通過。如果name為空字符串,則所有的日志都能通過。
Filter 類 有 三個方法 :
- addFilter(filter) : 為 logger(logger..addFilter(filter)) 或者 handler(handler..addFilter(filter)) 增加過濾器
- removeFilter(filter) : 為 logger 或者 handler 刪除一個過濾器
- filter(record) : 表示是否要記錄指定的記錄?返回零表示否,非零表示是。一般自定義Filter需要繼承Filter基類,并重寫filter方法
Formatter
格式化日志的輸出;實例化:formatter = logging.Formatter(fmt=None,datefmt=None)
; 如果不指明 fmt,將默認(rèn)使用 ‘%(message)s’ ,如果不指明 datefmt,將默認(rèn)使用 ISO8601 日期格式。
其中 fmt 參數(shù) 有以下選項:
%(name)s | Logger的名字 |
---|---|
%(levelno)s | 數(shù)字形式的日志級別 |
%(levelname)s | 文本形式的日志級別;如果是logger.debug則它是DEBUG,如果是logger.error則它是ERROR |
%(pathname)s | 調(diào)用日志輸出函數(shù)的模塊的完整路徑名,可能沒有 |
%(filename)s | 調(diào)用日志輸出函數(shù)的模塊的文件名 |
%(module)s | 調(diào)用日志輸出函數(shù)的模塊名 |
%(funcName)s | 調(diào)用日志輸出函數(shù)的函數(shù)名 |
%(lineno)d | 調(diào)用日志輸出函數(shù)的語句所在的代碼行 |
%(created)f | 當(dāng)前時間,用UNIX標(biāo)準(zhǔn)的表示時間的浮 點數(shù)表示 |
%(relativeCreated)d | 輸出日志信息時的,自Logger創(chuàng)建以 來的毫秒數(shù) |
%(asctime)s | 字符串形式的當(dāng)前時間。默認(rèn)格式是 “2003-07-08 16:49:45,896”。逗號后面的是毫秒 |
%(thread)d | 線程ID。可能沒有 |
%(threadName)s | 線程名。可能沒有 |
%(process)d | 進程ID。可能沒有 |
%(message)s | 用戶輸出的消息; 假如有l(wèi)ogger.warning("NO Good"),則在%(message)s位置上是字符串NO Good |
例如:
formatter = logging.Formatter('%(asctime)s %(levelname)-8s: %(message)s') # -表示右對齊 8表示取8位
handler.formatter = formatter
datefmt 參數(shù) 有以下選項:
參數(shù) | 含義 |
---|---|
%y | 兩位數(shù)的年份表示(00-99) |
%Y | 四位數(shù)的年份表示(000-9999) |
%m | 月份(01-12) |
%d | 月內(nèi)中的一天(0-31) |
%H | 24小時制小時數(shù)(0-23) |
%I | 12小時制小時數(shù)(01-12) |
%M | 分鐘數(shù)(00=59) |
%S 秒 | (00-59) |
例如:
formatter = logging.Formatter('%(asctime)s %(levelname)-8s: %(message)s') # -表示右對齊 8表示取8位 handler.formatter = formatter
datefmt 參數(shù) 有以下選項:
參數(shù) | 含義 |
---|---|
%y | 兩位數(shù)的年份表示(00-99) |
%Y | 四位數(shù)的年份表示(000-9999) |
%m | 月份(01-12) |
%d | 月內(nèi)中的一天(0-31) |
%H | 24小時制小時數(shù)(0-23) |
%I | 12小時制小時數(shù)(01-12) |
%M | 分鐘數(shù)(00=59) |
%S 秒 | (00-59) |
例子:
formatter = logging.Formatter("%(asctime)s %(levelname)s %(message)s","%Y%m%d-%H:%M:%S") handler.formatter = formatter
logging 的配置
- conf 形式的配置
在 loguser.conf 中 寫入相關(guān)的信息
[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時,直接導(dǎo)入配置文件即可
from logging import config with open('./loguser.conf', 'r', encoding='utf-8') as f: ## 加載配置 config.fileConfig(f) ## 創(chuàng)建同名Logger,其按照配置文件的handle,formatter,filter方法初始化 logger = logging.getLogger(name="fileLogger")
- yaml 形式配置文件
在 loguser.yaml文件 中 配置相關(guān)信息
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為默認(rèn)情況下的輸出配置, 當(dāng)logging.getLogger("fileLoggername")里面的fileLoggername沒有傳值的時候, # 就是用的這個默認(rèn)的root,如logging.getLogger(__name__)或logging.getLogger() root: level: DEBUG handlers: [console]
同樣的可以通過導(dǎo)入 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() # 子記錄器的名字與配置文件中l(wèi)oggers字段內(nèi)的保持一致 # loggers: # my_testyaml: # level: DEBUG # handlers: [console, info_file_handler,error_file_handler] my_testyaml = logging.getLogger("my_testyaml")
logging 和 print 的區(qū)別
看起來logging要比print復(fù)雜多了,那么為什么推薦在項目中使用 logging 記錄日志而不是使用print 輸出程序信息呢。
相比與print logging 具有以下優(yōu)點:
- 可以通過設(shè)置不同的日志等級,在 release 版本中只輸出重要信息,而不必顯示大量的調(diào)試信息;
- print 將所有信息都輸出到標(biāo)準(zhǔn)輸出中,嚴(yán)重影響開發(fā)者從標(biāo)準(zhǔn)輸出中查看其它數(shù)據(jù);logging 則可以由開發(fā)者決定將信息輸出到什么地方,以及怎么輸出;
- 和 print 相比,logging 是線程安全的。(python 3中 print 也是線程安全的了,而python 2中的print不是)(線程安全是指在多線程時程序不會運行混亂;而python 2 中的print 分兩步打印信息,第一打印字符串,第二打印換行符,如果在這中間發(fā)生線程切換就會產(chǎn)生輸出混亂。這就是為什么python2的print不是原子操作,也就是說其不是線程安全的)印信息,第一打印字符串,第二打印換行符,如果在這中間發(fā)生線程切換就會產(chǎn)生輸出混亂。這就是為什么python2的print不是原子操作,也就是說其不是線程安全的)
主要參考資料
https://blog.csdn.net/weixin_41010198/article/details/89356417
https://www.cnblogs.com/chenyibai/p/10676574.html
到此這篇關(guān)于Python 內(nèi)置logging 使用詳細講的文章就介紹到這了,更多相關(guān)Python 內(nèi)置logging內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python控制windows系統(tǒng)音量實現(xiàn)實例
這篇文章主要介紹了Python控制windows系統(tǒng)音量實現(xiàn)實例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2023-01-01python實現(xiàn)小世界網(wǎng)絡(luò)生成
今天小編就為大家分享一篇python實現(xiàn)小世界網(wǎng)絡(luò)生成,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-11-11python 實現(xiàn)圖片旋轉(zhuǎn) 上下左右 180度旋轉(zhuǎn)的示例
今天小編就為大家分享一篇python 實現(xiàn)圖片旋轉(zhuǎn) 上下左右 180度旋轉(zhuǎn)的示例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-01-01