前言
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日志記錄的邏輯調用過程
-
記錄日志通過調用logger.debug等方法;
-
首先判斷本條記錄的日志級別是否大於設置的級別,如果不是,直接pass,不再執行;
-
將日志信息當做參數創建一個LogRecord日志記錄對象;
-
將LogRecord對象經過logger過濾器過濾,如果被過濾掉則pass;
-
日志記錄對象被Handler處理器的過濾器過濾;
-
判斷本條記錄的日志級別是否大於Handler處理器設置的級別,如果不是,直接pass,不再執行,最后調用處理器的emit方法處理日志記錄;
配置logger
- 通過代碼進行完整配置,主要是通過getLogger方法實現,但不好修改;
- 通過basicConfig方法實現,這種方式快速但不夠層次分明;
- 通過logging.config.fileConfig(filepath),文件配置。
- 通過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() # 啟動監聽服務器
