提供日誌記錄的介面和眾多處理模組,供使用者儲存各種格式的日誌,幫助偵錯程式或記錄程式運行過程中的輸出資訊。
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物件 |
新增一個filter物件 | |
移除一個filter物件 | |
設定DEBUG等級的日誌記錄 | |
設定INFO等級的日誌記錄 | |
設定WARNING層級的日誌記錄 | |
設定ERROR等級的日誌記錄 | |
#設定CRITICAL等級的日誌記錄 | |
輸出堆疊追蹤資訊 | |
設定一個自訂的level參數來建立一個日誌記錄 |
#Handler.setLevel() | 設定處理器將會處理的日誌訊息的最低嚴重程度 |
為處理器設定一個格式物件 | |
為處理器新增一個過濾器物件 | |
為處理器刪除一個過濾器物件 | |
將日誌訊息傳送到輸出Stream,如std.out,std.err | |
將日誌訊息傳送到磁碟文件,預設情況文件大小會無線成長 | |
將日誌訊息傳送到磁碟文件,支援日誌檔案以大小切割 | |
將日誌訊息傳送到磁碟文件,並支援日誌檔案按時間切割 | |
將日誌訊息通過GET或POST的方式傳送給一個HTTP伺服器 | |
將日誌訊息傳送email位址 |
%(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)-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 具有以下优点:
可以通过设置不同的日志等级,在 release 版本中只输出重要信息,而不必显示大量的调试信息;
print 将所有信息都输出到标准输出中,严重影响开发者从标准输出中查看其它数据;logging 则可以由开发者决定将信息输出到什么地方,以及怎么输出;
和 print 相比,logging 是线程安全的。(python 3中 print 也是线程安全的了,而python 2中的print不是)(线程安全是指在多线程时程序不会运行混乱;而python 2 中的print 分两步打印信息,第一打印字符串,第二打印换行符,如果在这中间发生线程切换就会产生输出混乱。这就是为什么python2的print不是原子操作,也就是说其不是线程安全的)印信息,第一打印字符串,第二打印换行符,如果在这中间发生线程切换就会产生输出混乱。这就是为什么python2的print不是原子操作,也就是说其不是线程安全的)
以上是Python內建logging怎麼使用的詳細內容。更多資訊請關注PHP中文網其他相關文章!