Python?內(nèi)置logging?使用詳細介紹
logging 的主要作用
提供日志記錄的接口和眾多處理模塊,供用戶存儲各種格式的日志,幫助調(diào)試程序或者記錄程序運行過程中的輸出信息。
logging 日志等級
logging 日志等級分為五個等級,優(yōu)先級從高到低依次是 :
**CRITICAL; ** 程序嚴重錯誤
**ERROR; **程序錯誤/部分功能錯誤
**WARNING; **程序有發(fā)生錯誤的可能
**INFO; **程序正常運行時的信息
DEBUG程序調(diào)試信息
默認的日志的記錄等級為 WARNING, 即當日志的等級大于獲等于 WARNING 時才會被記錄。
一般常用的記錄等級為 INFO,其用于記錄程序的正常運行的一些信息(類似于print)。
當日志的等級達到 WARNING 以上時,表明此時程序不能正常運行;
logging 的基礎(chǔ)函數(shù)
logging.basicConfig(**kwargs)
在沒有顯式的進行創(chuàng)建記錄器(logger)時,會默認創(chuàng)建一個root logger,而logging.basicConfig(**kwargs) 可以創(chuàng)建帶有默認的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() 時會使用默認的 root logger,并會自動調(diào)用 自定義的或者默認的logging.basicConfig(**kwargs) 初始化 root logger。
自定義的 logging.basicConfig(**kwargs) 中的參數(shù) 有以下的主要的選項:
| 參數(shù) | 功能 |
|---|---|
| filename | 指定保存日志的文件名,用指定文件名創(chuàng)建一個FileHandler,記錄的日志會保存到該文件中 |
| format | 指定輸出的格式和內(nèi)容,默認是以冒號分割的levalname、name 和 message |
| datefmt | 使用指定的日期/時間格式,與 time.strftime() 所接受的格式相同。 |
| level | 指定根日志記錄器級別,默認為 logging.WARNING |
| stream | 指定日志的輸出流,可以指定輸出到sys.stderr,std.stdout 或 文件,默認輸出到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) 實例化記錄器
默認情況下,記錄器采用層級結(jié)構(gòu),通過 . 來區(qū)分不同的層級。比如 有個名叫 foo 的記錄器 則 foo.a 和 foo.b 都是 foo 的子級記錄器。當然,最開始的或者說最上層的記錄器就是 root logger。如果 name=None,構(gòu)建的是root logger。
可以直接用當前模塊的名稱當作記錄器的名字 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默認的級別為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將日志信息輸出到目標位置,目標位置可以是sys.stdout和文件等(這與logging.basicConfig(**kwargs) 設(shè)置中不太一致)。
- 一個Logger可以設(shè)置不同的Handler,而不同的Handler可以將日志輸出到不同的位置(不同的日志文件),并且每個Handler都可以設(shè)置自己的filter從而實現(xiàn)日志過濾,保留實際項目中需要的日志。同時每個Handler也可以設(shè)置不同的Formatter,在每個Formatter實現(xiàn)同一條日志以不同的格式輸出到不同的地方。
Handle
處理器;其可以控制記錄的日志輸出到什么地方(標準輸出/文件/...),同時處理器也可以添加 過濾器(filter)和格式控制器(formatter)來控制輸出的內(nèi)容和輸出的格式。
其具有幾種常見的處理器:
- logging.StreamHandler 標準流處理器,將消息發(fā)送到標準輸出流、錯誤流 --> logging.StreamHandler(sys.stdout) # sys.stdout 表示的是指向控制臺即標準輸出;當我們在 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è)置處理器將會處理的日志消息的最低嚴重級別 |
| Handler.setFormatter() | 為處理器設(shè)置一個格式對象 |
| Handler.addFilter() | 為處理器添加一個過濾器對象 |
| Handler.removeFilter() | 為處理器刪除一個過濾器對象 |
| logging.StramHandler() | 將日志消息發(fā)送到輸出Stream,如std.out,std.err |
| logging.FilterHandler() | 將日志消息發(fā)送到磁盤文件,默認情況文件大小會無線增長 |
| RotationFileHandler() | 將日志消息發(fā)送到磁盤文件,支持日志文件按大小切割 |
| TimeRotatingFileHandler() | 將日志消息發(fā)送到磁盤文件,并支持日志文件按時間切割 |
| logging.handers.HTTPHandler() | 將日志消息通過GET或POST的方式發(fā)送給一個HTTP服務器 |
| 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,將默認使用 ‘%(message)s’ ,如果不指明 datefmt,將默認使用 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 | 當前時間,用UNIX標準的表示時間的浮 點數(shù)表示 |
| %(relativeCreated)d | 輸出日志信息時的,自Logger創(chuàng)建以 來的毫秒數(shù) |
| %(asctime)s | 字符串形式的當前時間。默認格式是 “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時,直接導入配置文件即可
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為默認情況下的輸出配置, 當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()
# 子記錄器的名字與配置文件中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復雜多了,那么為什么推薦在項目中使用 logging 記錄日志而不是使用print 輸出程序信息呢。
相比與print logging 具有以下優(yōu)點:
- 可以通過設(shè)置不同的日志等級,在 release 版本中只輸出重要信息,而不必顯示大量的調(diào)試信息;
- print 將所有信息都輸出到標準輸出中,嚴重影響開發(fā)者從標準輸出中查看其它數(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實現(xiàn)輕松合并doc為txt的示例代碼
這篇文章主要為大家詳細介紹了如何利用Python編程語言和wxPython模塊,打開指定文件夾中的DOC文檔,并將它們的內(nèi)容合并成一個便捷的TXT文檔,需要的可以參考下2024-03-03
python文件轉(zhuǎn)為exe文件的方法及用法詳解
py2exe是一個將python腳本轉(zhuǎn)換成windows上的可獨立執(zhí)行的可執(zhí)行程序(*.exe)的工具,這樣,你就可以不用裝python而在windows系統(tǒng)上運行這個可執(zhí)行程序。本文重點給大家介紹python文件轉(zhuǎn)為exe文件的方法,感興趣的朋友跟隨小編一起看看吧2019-07-07
Pandas DataFrame replace替換后無效的解決
這篇文章主要介紹了Pandas DataFrame replace替換后無效的解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-08-08
Python?Melt函數(shù)將寬格式的數(shù)據(jù)表轉(zhuǎn)換為長格式
在數(shù)據(jù)處理和清洗中,melt函數(shù)是Pandas庫中一個強大而靈活的工具,它的主要功能是將寬格式的數(shù)據(jù)表轉(zhuǎn)換為長格式,從而更方便進行分析和可視化,本文將深入探討melt函數(shù)的用法、參數(shù)解析以及實際應用場景2023-12-12

