本文首發(fā)于Gevin的博客
原文鏈接:Python 日志功能詳解
未經 Gevin 授權低淡,禁止轉載
軟件開發(fā)中通過日志記錄程序的運行情況是一個開發(fā)的好習慣椅邓,對于錯誤排查和系統(tǒng)運維都有很大幫助炎咖。Python標準庫自帶日志模塊厕吉,已經足夠強大受扳,大部分情況下序无,python程序的日志功能直接調用標準庫的日志模塊即可验毡『獯矗《The Hitchhiker’s Guide to Python》已對“日志”進行了詳細闡述,python的官方文檔也對日志做了說明晶通,但Gevin依然感覺璃氢,通過這些英文資料,還不能讓初學者在短時間迅速掌握python日志模塊的使用狮辽,因此按照自己的思路一也,把相關內容做了如下整理,并附加一些Gevin認為文檔中沒有說明清楚的內容喉脖。
1. 基本用法
如果開發(fā)輕量級的應用椰苟,對日志的需求也比較簡單,直接參考如下示例树叽,在相關代碼邏輯中加入日志功能即可:
#!/usr/local/bin/python
# -*- coding: utf-8 -*-
import logging
logging.debug('debug message')
logging.info('info message')
logging.warn('warn message')
logging.error('error message')
logging.critical('critical message')
output:
WARNING:root:warn message
ERROR:root:error message
CRITICAL:root:critical message
默認情況下舆蝴,logging模塊將日志打印到屏幕上,日志級別為WARNING(即只有日志級別等于或高于WARNING的日志信息才會輸出)题诵,日志格式為 warning level:instance name:warning message
1.1 將日志記錄到文件中
將日志記錄到文件中洁仗,只需在調用logging模塊記錄日志前,做個簡單的配置即可:
#!/usr/local/bin/python
# -*- coding: utf-8 -*-
import logging
# 配置日志文件和日志級別
logging.basicConfig(filename='logger.log', level=logging.INFO)
logging.debug('debug message')
logging.info('info message')
logging.warn('warn message')
logging.error('error message')
logging.critical('critical message')
本樣例在記錄日志前性锭,通過logging.basicConfig
做簡單配置赠潦,將日志記錄到日志文件logger.log
中,也修改了默認的日志等級草冈,高于或等于INFO級別的日志信息她奥,都會記錄到日志文件中。
2. 更加完善的日志功能
2.1 幾個關鍵概念
如果要更加靈活的使用日志模塊疲陕,首先要了解日志模塊是怎樣工作的方淤。 Logger
,Handler
蹄殃,Formatter
和Filter
是日志模塊的幾個基本概念携茂,日志模塊的工作原理要從這四個基本概念說起。
- Logger 即記錄器诅岩,Logger提供了日志相關功能的調用接口讳苦。
- Handler 即處理器,將(記錄器產生的)日志記錄發(fā)送至合適的目的地吩谦。
- Filter 即過濾器鸳谜,提供了更好的粒度控制,它可以決定輸出哪些日志記錄式廷。
- Formatter 即格式化器咐扭,指明了最終輸出中日志記錄的格式。
2.1.1 Logger
Logger 即“記錄器”,Logger對象實例是日志記錄功能的載體蝗肪,如:
#!/usr/local/bin/python
# -*- coding: utf-8 -*-
import logging
logger = logging.getLogger('simple_example')
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')
值得一提的是袜爪,Logger對象從不直接實例化,而是通過模塊級的功能logging.getLogger(name)
創(chuàng)建Logger實例薛闪。調用 logging.getLogger(name)
功能時辛馆,如果傳入的name
參數(shù)值相同,則總是返回同一個Logger對象實例的引用豁延。
如果沒有顯式的進行創(chuàng)建昙篙,則默認創(chuàng)建一個root logger,并應用默認的日志級別(WARN)诱咏、默認的處理器Handler(StreamHandler苔可,即將日志信息打印輸出在標準輸出上),和默認的格式化器Formatter(默認的格式即為第一個簡單使用程序中輸出的格式)袋狞。
Logger類包含的成員和方法可以查看官方文檔硕蛹。
2.1.2 Handler
Handler 將日志信息發(fā)送到設置的位置,可以通過Logger對象的addHandler()
方法為Logger對象添加0個或多個handler硕并。一種日志的典型應用場景為蛮艰,系統(tǒng)希望將所有的日志信息保存到log文件中棋傍,其中日志等級等于或高于ERROR
的消息還要在屏幕標準輸出上顯示,日志等級為CRITICAL
的還需要發(fā)送郵件通知评肆;這種場景就需要3個獨立的handler來實現(xiàn)需求乙濒,這三個handler分別與指定的日志等級或日志位置做響應
需要一提的是陕赃,為Logger配置的handler不能是Handler基類對象,而是Handler的子類對象颁股,常用的Handler為StreamHandler
, FileHandler
么库, 和NullHandler
,Handler的全部子類及詳細介紹可以查看官方文檔相應頁面如果需要了解更多關于Handler的信息甘有,直接查看官方文檔即可诉儒。
2.1.3 Formatter
Formatter 用于設置日志輸出的格式,與前兩個基本概念不同的是亏掀,該類可以直接初始化對象忱反,即 formatter=logging.Formatter(fmt=None, datefmt=None)
,創(chuàng)建formatter時滤愕,傳入分別fmt
和datefmt
參數(shù)來修改日志格式和時間格式温算,默認的日志格式為%(asctime)s - %(levelname)s - %(message)s
,默認的時間格式為%Y-%m-%d %H:%M:%S
2.1.4 Filter
Filter 可用于Logger對象或Handler對象间影,用于提供比日志等級更加復雜的日志過濾方式注竿。默認的filter只允許在指定logger層級下的日志消息通過過濾。例如,如果把filter設置為filter=logging.Filter('A.B')
巩割,則logger ‘A.B’, ‘A.B.C’, ‘A.B.C.D’, ‘A.B.D’ 產生的日志信息可以通過過濾裙顽,但'A.BB', 'B.A.B'均不行。如果以空字符串初始化filter喂分,則所有的日志消息都可以通過過濾锦庸。
Filter在日志功能配置中是非必須的,只在對日志消息過濾需求比較復雜時配置使用即可蒲祈。
2.2 日志產生流程
日志產生的流程邏輯參考下圖即可:
2.3 日志模塊的使用
日志模塊使用的關鍵是“日志的配置”甘萧,日志配置好后,只要調用logger.INFO()
, logger.ERROR()
等方法即可創(chuàng)建日志內容梆掸。
開發(fā)者可以通過三種方法配置日志模塊:
- 在Python代碼中顯示創(chuàng)建loggers, handlers, formatters甚至filters扬卷,并調用這幾個對象中的各個配置函數(shù)來完成日志配置
- 將配置信息寫到配置文件中,然后讀取配置文件信息來完成日志配置
- 將配置信息寫到一個
Dict
中酸钦,然后讀取這個配置字典來完成日志配置
2.3.1 通過代碼配置并使用日志模塊
通過代碼配置日志模塊簡單方便怪得,但如果需要修改配置時,需要改代碼卑硫,因此不建議在大型項目中使用這種方法徒恋。
通過代碼配置日志模塊可以很好的理解日志模塊的工作原理,用于學習欢伏,是一個很好的案例入挣,因此Gevin也在下文中對此詳細介紹。
1. 創(chuàng)建Logger
import logging
# create logger
logger = logging.getLogger('simple_example')
# Set default log level
logger.setLevel(logging.DEBUG)
2. 創(chuàng)建Handler
# create console handler and set level to warn
ch = logging.StreamHandler()
ch.setLevel(logging.WARN)
3. 創(chuàng)建Fomatter
# create formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
4. 配置Logger
# add formatter to ch
ch.setFormatter(formatter)
# add ch to logger
# The final log level is the higher one between the default and the one in handler
logger.addHandler(ch)
5. 使用日志模塊
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')
6. 完整的例子
#!/usr/local/bin/python
# -*- coding: utf-8 -*-
import logging
# create logger
logger = logging.getLogger('simple_example')
# Set default log level
logger.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.WARN)
ch2 = logging.FileHandler('logging.log')
ch2.setLevel(logging.INFO)
# create formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# add formatter to ch
ch.setFormatter(formatter)
ch2.setFormatter(formatter)
# add ch to logger
# The final log level is the higher one between the default and the one in handler
logger.addHandler(ch)
logger.addHandler(ch2)
# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')
2.3.2 通過配置文件配置并使用日志模塊
通過配置文件配置日志模塊時硝拧,配置文件通常使用.ini
格式径筏,日志模塊需要調用fileConfig
,即logging.config.fileConfig('logging_config.ini')
障陶,然后logger的使用方法與上面相同:
#!/usr/local/bin/python
# -*- coding: utf-8 -*-
import logging
import logging.config
logging.config.fileConfig('logging_config.ini')
# create logger
logger = logging.getLogger('root')
# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')
其中滋恬,logging_config.ini
文件內容如下:
[loggers]
keys=root,simpleExample
[handlers]
keys=consoleHandler
[formatters]
keys=simpleFormatter
[logger_root]
level=DEBUG
handlers=consoleHandler
[logger_simpleExample]
level=INFO
handlers=consoleHandler
qualname=simpleExample
propagate=0
[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)
[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
通過配置文件配置日志模塊,邏輯與代碼中配置一樣抱究,也是把logger
, handler
和formatter
定義好恢氯,然后組裝到一起即可,無非ini配置和代碼配置時的語法不通而已媳维,開發(fā)者在基于ini文件配置日志模塊時酿雪,只要參考上面例子做相應修改即可。
2.3.3 通過Dict對象配置并使用日志模塊
基于Dict對象配置日志模塊在python中應用廣泛侄刽,很多Django或Flask項目都采用這種方式指黎,但很多官方文檔對這種方法介紹并不多,因此州丹,本文提供一個使用樣例醋安,以后開發(fā)中參考該樣例修改一下即可杂彭。
#!/usr/local/bin/python
# -*- coding: utf-8 -*-
import logging
import logging.config
config = {
'version': 1,
'formatters': {
'simple': {
'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
},
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'level': 'DEBUG',
'formatter': 'simple'
},
'file': {
'class': 'logging.FileHandler',
'filename': 'logging.log',
'level': 'DEBUG',
'formatter': 'simple'
},
},
'loggers':{
'root': {
'handlers': ['console'],
'level': 'DEBUG',
# 'propagate': True,
},
'simple': {
'handlers': ['console', 'file'],
'level': 'WARN',
}
}
}
logging.config.dictConfig(config)
print 'logger:'
logger = logging.getLogger('root')
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')
print 'logger2:'
logger2 = logging.getLogger('simple')
logger2.debug('debug message')
logger2.info('info message')
logger2.warn('warn message')
logger2.error('error message')
logger2.critical('critical message')
注:
日志的嚴重等級
Log Level如下,嚴重等級為NOTSET
, DEBUG
, INFO
, WARNING
, ERROR
, CRITICAL
, 嚴重程度依次遞增
CRITICAL: 50
ERROR: 40
WARNING: 30
INFO: 20
DEBUG: 10
NOTSET: 0
修改日志消息的格式
日志的默認顯示格式為:%(asctime)s - %(name)s - %(levelname)s - %(message)s
吓揪,如果只想顯示日志等級和日志信息亲怠,可以把格式改為:%(levelname)s:%(message)s
,想了解全部Formatter中的可用變量柠辞,請查閱LogRecord attributes
日期時間的默認格式是ISO8601团秽,修改日期時間格式請參考 time.strftime()