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

前言

python的logging模塊是用來設置日誌的,是python的標準模塊。html

系列文章

日誌記錄的級別

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

  • 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() # 啓動監聽服務器

參考

相關文章
相關標籤/搜索