python模塊分析之logging日志(四)


前言

python的logging模塊是用來設置日志的,是python的標准模塊。

系列文章

日志記錄的級別

  • DEBUG:優先級10,記錄調試的詳細信息,只在調試時開啟;

  • INFO:優先級20,記錄普通的消息,包括錯誤和警告等等;

  • WARNING:優先級30,記錄相關的警告信息;

  • ERROR:優先級40,記錄錯誤信息,程序崩潰;

  • CRITICAL:優先級50,記錄錯誤信息;

如果不設置級別的話,默認為warning,系統記錄設置的日志級別優先級以上的日志信息。

logging模塊的主要結構

查看logging的源碼,可知主要有四個類實現功能;

  • Loggers:提供應用程序直接使用的接口,如相關的配置設置;

  • Handlers:將Loggers產生的日志傳到指定位置,設置日志保存的位置;

  • Filters:對輸出日志進行過濾操作;

  • Formatters:控制日志的輸出格式;

Formatters

Formatter對象定義了日志的輸出格式,有多種可選參數。

%(name)s Logger的名字
%(levelno)s 數字形式的日志級別
%(levelname)s 文本形式的日志級別
%(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 用戶輸出的消息

# 定義一個輸出格式的對象
formatter = logging.Formatter('%(asctime)s %(levelname)s: %(message)s',datefmt='%Y-%m-%d %H:%M:%S', style='%')

# Formatter參數:
fmt:定義輸出的日志信息的格式;
datefmt:定義時間信息的格式,默認為'%Y-%m-%d %H:%M:%S';
style:定義格式化輸出的占位符,默認是%(name)格式,可選{}或$格式

Handlers日志處理器

日志處理器用來處理日志的具體流向,是輸出到文件中還是到標准輸出等,它通過設置Formatter控制輸出格式,添加filters過濾日志;

  • 常見的處理器有兩種:

StreamHandler:用於向控制台打印日志;

FileHandler:用於向日志文件打印日志;

  • 其它的處理器
RotatingHandler:logging.handlers.RotatingHandler;日志回滾方式,支持日志文件最大數量和日志文件回滾
TimeRotatingHandler:logging.handlers.TimeRotatingHandler;日志回滾方式,在一定時間區域內回滾日志文件
SocketHandler:logging.handlers.SocketHandler;遠程輸出日志到TCP/IP sockets
DatagramHandler:logging.handlers.DatagramHandler;遠程輸出日志到UDP sockets
SMTPHandler:logging.handlers.SMTPHandler;遠程輸出日志到郵件地址
SysLogHandler:logging.handlers.SysLogHandler;日志輸出到syslog
NTEventLogHandler:logging.handlers.NTEventLogHandler;遠程輸出日志到Windows NT/2000/XP的事件日志
MemoryHandler:logging.handlers.MemoryHandler;日志輸出到內存中的指定buffer
HTTPHandler:logging.handlers.HTTPHandler;通過"GET"或者"POST"遠程輸出到HTTP服務器
from logging import Handler
handler = Handler() # 所有上述處理器的父類
print(handler.level) # 處理日志的等級
print(handler.name) # 處理日志的名字
print(handler.filters) # 處理器的日志過濾器
print(handler.formatter) # 日志的格式

handler.get_name() 
handler.set_name('')

handler.createLock() # 創建線程鎖
handler.acquire() # 獲取線程鎖
handler.release() # 釋放線程鎖
handler.setLevel('info') # 設置日志處理器的記錄級別
handler.setFormatter(fmt='') # 設置日志的輸出格式

handler.addFilter('') # 往處理器中添加過濾器
handler.removeFilter('') # 往處理器中移除過濾器

handler.emit('') # 日志記錄的處理邏輯,由子類實現

Logger日志對象

Logger管理着所有記錄日志的方法。

logger = getLogger(__name__) # 返回一個Logger實例
# logger = getLogger('root') # 以'root'為名字的日志對象在Logger對象中只有一個實例

# 如果是想調用以'root'為名字的日志對象輸出日志,可以直接導入
from logging import error, debug, warning, info, fatal, critical

print(logger.root) # 獲取根日志對象
print(logger.manager) # 獲取manager
print(logger.name) # 日志對象的名字
print(logger.level) # 日志記錄水平
print(logger.filters) # 日志過濾器列表
print(logger.handlers) # 日志處理器列表
print(logger.disabled)

logger.setLevel('info') # 設置日志記錄水平

logger.info('this is %s', 'info', exc_info=1) # 輸出日志信息,格式化輸出
logger.warning('') # 記錄warning信息
logger.error('') # 記錄error信息
logger.exception('') # 等價於logger.error('', exc_info=1)
logger.debug('') # 記錄debug信息
logger.critical('') # 記錄critical信息
logger.log('info','') # 直接指定級別

logger.addHandler('') # 添加處理器
logger.removeHandler('') # 移除處理器
logger.hasHandlers() # 判斷是否有處理器

logger的基本使用

import logging
import sys

def get_logger(appname):
    # 獲取logger實例,如果參數為空則返回root logger
    logger = logging.getLogger(appname)
    # 創建日志輸出格式
    formatter = logging.Formatter('%(asctime)s %(levelname)s: %(message)s')

    # 設置文件處理器,加載處理格式
    file_handler = logging.FileHandler("test.log") # 指定輸出的文件路徑
    file_handler.setFormatter(formatter)

    # 控制台日志
    console_handler = logging.StreamHandler(sys.stdout)
    console_handler.formatter = formatter

    # 為logger添加的日志處理器
    logger.addHandler(file_handler)
    logger.addHandler(console_handler)

    # 指定日志的最低輸出級別,默認為WARN級別
    logger.setLevel(logging.INFO)

if __name__  == "__main__":
    logger = get_logger('test')
    logger.debug('this is debug info')
    logger.info('this is information')
    logger.warn('this is warning message')
    logger.error('this is error message')
    logger.fatal('this is fatal message, it is same as logger.critical')
    logger.critical('this is critical message')

logger日志記錄的邏輯調用過程

  1. 記錄日志通過調用logger.debug等方法;

  2. 首先判斷本條記錄的日志級別是否大於設置的級別,如果不是,直接pass,不再執行;

  3. 將日志信息當做參數創建一個LogRecord日志記錄對象;

  4. 將LogRecord對象經過logger過濾器過濾,如果被過濾掉則pass;

  5. 日志記錄對象被Handler處理器的過濾器過濾;

  6. 判斷本條記錄的日志級別是否大於Handler處理器設置的級別,如果不是,直接pass,不再執行,最后調用處理器的emit方法處理日志記錄;

配置logger

  1. 通過代碼進行完整配置,主要是通過getLogger方法實現,但不好修改;
  2. 通過basicConfig方法實現,這種方式快速但不夠層次分明;
  3. 通過logging.config.fileConfig(filepath),文件配置。
  4. 通過dictConfig的字典方式配置,這是py3.2版本引入的新的配置方法;

使用文件方式配置

# logging.conf

[loggers]    # 定義日志的對象名稱是什么,注意必須定義root,否則報錯。
keys=root,main

[handlers]  # 定義處理器的名稱是什么,可以有多個,用逗號隔開
keys=consoleHandler

[formatters]  # 定義輸出格式對象的名稱,可以有多個,用逗號隔開
keys=simpleFormatter

[logger_root]  # 配置root對象的日志記錄級別和使用的處理器
level=INFO
handlers=consoleHandler

[logger_main] # 配置main對象的日志記錄級別和使用的處理器,qualname值得就是日志對象的名字
level=INFO
handlers=consoleHandler
qualname=main
propagate=0  # logger對象把日志記錄傳遞給所有相關的handler的時候,會(逐級向上)尋找這個logger和它所有的父logger的全部handler,propagate=1表示會繼續向上搜尋;propagate=0表示停止搜尋,這個參數涉及重復打印的坑。

[handler_consoleHandler] # 配置處理器consoleHandler
class=StreamHandler      
level=WARNING
formatter=simpleFormatter
args=(sys.stdout,)

[formatter_simpleFormatter]  # 配置輸出格式過濾器simpleFormatter
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
  • 注意:可以看到logger和Handler都可以設置日志級別,日志輸出是取最高級別。

使用字典形式配置

字典形式配置功能更加強大,也更加靈活。通過dictConfig函數,我們可以將其他格式的配置文件先轉化成字典,如json、YAML等。

import yaml
from logging.config import dictConfig
import os
filename = os.path.dirname(os.path.abspath(__file__))
with open(filename + '\logging.yml', 'r') as f:
    log = yaml.load(f.read())
    dictConfig(log)
# logging.yaml
version: 1
formatters:
  simple:
    format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
handlers:
  console:
    class: logging.StreamHandler
    level: DEBUG
    formatter: simple
    stream: ext://sys.stdout
  console_err:
    class: logging.StreamHandler
    level: DEBUG
    formatter: simple
    stream: ext://sys.stderr
loggers:
  simpleExample:
    level: DEBUG
    handlers: [console]
    propagate: no
root:
  level: DEBUG
  handlers: [console_err]

一些細節

  • logger的繼承

logging.getLogger(appname),同一個appname獲取到的logger對象是同一個,同時appname.name命名的logger繼承了appname的所有的屬性,可以不用重新配置logger。

  • 輸出錯誤信息的方法
try:
    raise
except:
    logger.error('this is error message',exc_info = True)
    # 或
    logger.exception("Failed to open sklearn.txt from logger.exception")

# 設置exc_info為True后,會自動將錯誤的信息也發送到日志中;
# 直接使用exception實現一樣的功能。

監聽logger配置更改

  • logging.config.listen(port)函數可以讓應用程序在一個socket上監聽新的配置信息,達到在運行時改變配置,而不用重啟應用程序的目的。
import logging.config  
import logging  
import time

logging.config.fileConfig("logging.conf")  
logger = logging.getLogger("test.listen")

t = logging.config.listen(9999)  
t.setDaemon(True)  
t.start() # 啟動監聽服務器

參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM