首頁  >  文章  >  後端開發  >  Python內建logging怎麼使用

Python內建logging怎麼使用

王林
王林轉載
2023-05-10 10:55:051357瀏覽

logging 的主要功能

提供日誌記錄的介面和眾多處理模組,供使用者儲存各種格式的日誌,幫助偵錯程式或記錄程式運行過程中的輸出資訊。

logging 日誌等級

logging 日誌等級分為五個等級,優先順序由高到低依序為:

**CRITICAL; ** 程式嚴重錯誤

**ERROR; **程式錯誤/部分功能錯誤

**WARNING; **程式有發生錯誤的可能

**INFO; **程式正常執行時的資訊

DEBUG程式調試資訊

預設的日誌的記錄等級為WARNING, 即當日誌的等級大於取得等於WARNING 時才會被記錄。

一般常用的記錄等級為 INFO,其用於記錄程式的正常運作的一些資訊(類似於print)。

當日誌的等級達到WARNING 以上時,表示此時程式無法正常運作;

logging 的基礎函數

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')

logging 的四大元件(類別)

Logger

除了根記錄器(root logger)外,最主要的是可以自行建立日誌記錄器。

透過模組層級的函數 logging.getLogger(name) 實例化記錄器

預設情況下,記錄器採用層級結構,透過. 來區分不同的層級。例如 有個名叫 foo 的記錄器 則 foo.afoo.b 都是 foo 的子級記錄器。當然,最開始的或最上層的記錄器就是 root logger。如果 name=None,建構的是root logger。

可以直接用目前模組的名稱當作記錄器的名字 logging.getLogger(__name__)

子級記錄器通常不需要單獨設定日誌等級以及Handler ,如果子級記錄器沒有單獨設置,則它的行為會委託給父級。比方說,記錄器foo的等級為INFO,而foo.afoo.b 都不設定日誌等級。此時foo.afoo.b 會遵循foo 的等級設置,即只記錄大於等於INFO等級的日誌;而如果foo也沒設定的話,就會找到根記錄器root logger,root預設的等級為WARGING。

logger類別的一些常用的方法

##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 結合後面要介紹的其他的三個元件可以實現以下的功能:

  • Logger需要透過handler將日誌資訊輸出到目標位置,目標位置可以是sys. stdout和檔案等(這與logging.basicConfig(**kwargs) 設定中不太一致)。

  • 一個Logger可以設定不同的Handler,而不同的Handler可以將日誌輸出到不同的位置(不同的日誌檔案),並且每個Handler都可以設定自己的filter從而實作日誌過濾,保留實際專案中所需的日誌。同時每個Handler也可以設定不同的Formatter,在每個Formatter實作相同日誌以不同的格式輸出到不同的地方。

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 類別的一些常用的方法

方法 功能描述
Logger.setLevel() 設定日誌器(Logger)將會處理的日誌訊息等級
Logger.addHandler() 新增一個handler物件
Logger.removeHandler() 移除一個handler物件
##Handler.setFormatter()為處理器設定一個格式物件Handler.addFilter()為處理器新增一個過濾器物件Handler.removeFilter()為處理器刪除一個過濾器物件#logging.StramHandler()將日誌訊息傳送到輸出Stream,如std.out,std.errlogging.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 类 有 三个方法 :

  • 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 参数 有以下选项:

#Handler.setLevel() 設定處理器將會處理的日誌訊息的最低嚴重程度
%(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

logging 的配置

  • 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复杂多了,那么为什么推荐在项目中使用 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中文網其他相關文章!

陳述:
本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除