本文首發於Gevin的博客javascript
原文連接:Python 日誌功能詳解html
未經 Gevin 受權,禁止轉載java
軟件開發中經過日誌記錄程序的運行狀況是一個開發的好習慣,對於錯誤排查和系統運維都有很大幫助。Python標準庫自帶日誌模塊,已經足夠強大,大部分狀況下,python程序的日誌功能直接調用標準庫的日誌模塊便可。《The Hitchhiker’s Guide to Python》已對「日誌」進行了詳細闡述,python的官方文檔也對日誌作了說明,但Gevin依然感受,經過這些英文資料,還不能讓初學者在短期迅速掌握python日誌模塊的使用,所以按照本身的思路,把相關內容作了以下整理,並附加一些Gevin認爲文檔中沒有說明清楚的內容。python
若是開發輕量級的應用,對日誌的需求也比較簡單,直接參考以下示例,在相關代碼邏輯中加入日誌功能便可:bash
#!/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:app
WARNING:root:warn message
ERROR:root:error message
CRITICAL:root:critical message複製代碼
默認狀況下,logging模塊將日誌打印到屏幕上,日誌級別爲WARNING(即只有日誌級別等於或高於WARNING的日誌信息纔會輸出),日誌格式爲 warning level:instance name:warning message
運維
將日誌記錄到文件中,只需在調用logging模塊記錄日誌前,作個簡單的配置便可:ide
#!/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級別的日誌信息,都會記錄到日誌文件中。函數
若是要更加靈活的使用日誌模塊,首先要了解日誌模塊是怎樣工做的。 Logger
,Handler
,Formatter
和Filter
是日誌模塊的幾個基本概念,日誌模塊的工做原理要從這四個基本概念提及。post
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)
建立Logger實例。調用 logging.getLogger(name)
功能時,若是傳入的name
參數值相同,則老是返回同一個Logger對象實例的引用。
若是沒有顯式的進行建立,則默認建立一個root logger,並應用默認的日誌級別(WARN)、默認的處理器Handler(StreamHandler,即將日誌信息打印輸出在標準輸出上),和默認的格式化器Formatter(默認的格式即爲第一個簡單使用程序中輸出的格式)。
Logger類包含的成員和方法能夠查看官方文檔。
Handler 將日誌信息發送到設置的位置,能夠經過Logger對象的addHandler()
方法爲Logger對象添加0個或多個handler。一種日誌的典型應用場景爲,系統但願將全部的日誌信息保存到log文件中,其中日誌等級等於或高於ERROR
的消息還要在屏幕標準輸出上顯示,日誌等級爲CRITICAL
的還須要發送郵件通知;這種場景就須要3個獨立的handler來實現需求,這三個handler分別與指定的日誌等級或日誌位置作響應
須要一提的是,爲Logger配置的handler不能是Handler基類對象,而是Handler的子類對象,經常使用的Handler爲StreamHandler
, FileHandler
, 和NullHandler
,Handler的所有子類及詳細介紹能夠查看官方文檔相應頁面若是須要了解更多關於Handler的信息,直接查看官方文檔便可。
Formatter 用於設置日誌輸出的格式,與前兩個基本概念不一樣的是,該類能夠直接初始化對象,即 formatter=logging.Formatter(fmt=None, datefmt=None)
,建立formatter時,傳入分別fmt
和datefmt
參數來修改日誌格式和時間格式,默認的日誌格式爲%(asctime)s - %(levelname)s - %(message)s
,默認的時間格式爲%Y-%m-%d %H:%M:%S
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在日誌功能配置中是非必須的,只在對日誌消息過濾需求比較複雜時配置使用便可。
日誌產生的流程邏輯參考下圖便可:
日誌模塊使用的關鍵是「日誌的配置」,日誌配置好後,只要調用logger.INFO()
, logger.ERROR()
等方法便可建立日誌內容。
開發者能夠經過三種方法配置日誌模塊:
Dict
中,而後讀取這個配置字典來完成日誌配置經過代碼配置日誌模塊簡單方便,但若是須要修改配置時,須要改代碼,所以不建議在大型項目中使用這種方法。
經過代碼配置日誌模塊能夠很好的理解日誌模塊的工做原理,用於學習,是一個很好的案例,所以Gevin也在下文中對此詳細介紹。
1. 建立Logger
import logging
# create logger
logger = logging.getLogger('simple_example')
# Set default log level
logger.setLevel(logging.DEBUG)複製代碼
2. 建立Handler
# create console handler and set level to warn
ch = logging.StreamHandler()
ch.setLevel(logging.WARN)複製代碼
3. 建立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')複製代碼
經過配置文件配置日誌模塊時,配置文件一般使用.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配置和代碼配置時的語法不通而已,開發者在基於ini文件配置日誌模塊時,只要參考上面例子作相應修改便可。
基於Dict對象配置日誌模塊在python中應用普遍,不少Django或Flask項目都採用這種方式,但不少官方文檔對這種方法介紹並很少,所以,本文提供一個使用樣例,之後開發中參考該樣例修改一下便可。
#!/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()