Python 日誌功能詳解

Python 日誌功能詳解

本文首發於Gevin的博客

原文鏈接:Python 日誌功能詳解

未經 Gevin 授權,禁止轉載

軟件開發中通過日誌記錄程序的運行情況是一個開發的好習慣,對於錯誤排查和系統運維都有很大幫助。Python標準庫自帶日誌模塊,已經足夠強大,大部分情況下,python程序的日誌功能直接調用標準庫的日誌模塊即可。《The Hitchhiker’s Guide to Python》已對“日誌”進行了詳細闡述,python的官方文檔也對日誌做了說明,但Gevin依然感覺,通過這些英文資料,還不能讓初學者在短時間迅速掌握python日誌模塊的使用,因此按照自己的思路,把相關內容做了如下整理,並附加一些Gevin認爲文檔中沒有說明清楚的內容。

1. 基本用法

如果開發輕量級的應用,對日誌的需求也比較簡單,直接參考如下示例,在相關代碼邏輯中加入日誌功能即可:

#!/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 幾個關鍵概念

如果要更加靈活的使用日誌模塊,首先要了解日誌模塊是怎樣工作的。 LoggerHandlerFormatterFilter是日誌模塊的幾個基本概念,日誌模塊的工作原理要從這四個基本概念說起。

  • Logger 即記錄器,Logger提供了日誌相關功能的調用接口。
  • Handler 即處理器,將(記錄器產生的)日誌記錄發送至合適的目的地。
  • 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)創建Logger實例。調用 logging.getLogger(name) 功能時,如果傳入的name參數值相同,則總是返回同一個Logger對象實例的引用。

如果沒有顯式的進行創建,則默認創建一個root logger,並應用默認的日誌級別(WARN)、默認的處理器Handler(StreamHandler,即將日誌信息打印輸出在標準輸出上),和默認的格式化器Formatter(默認的格式即爲第一個簡單使用程序中輸出的格式)。

Logger類包含的成員和方法可以查看官方文檔

2.1.2 Handler

Handler 將日誌信息發送到設置的位置,可以通過Logger對象的addHandler()方法爲Logger對象添加0個或多個handler。一種日誌的典型應用場景爲,系統希望將所有的日誌信息保存到log文件中,其中日誌等級等於或高於ERROR的消息還要在屏幕標準輸出上顯示,日誌等級爲CRITICAL的還需要發送郵件通知;這種場景就需要3個獨立的handler來實現需求,這三個handler分別與指定的日誌等級或日誌位置做響應

需要一提的是,爲Logger配置的handler不能是Handler基類對象,而是Handler的子類對象,常用的Handler爲StreamHandler, FileHandler, 和NullHandler,Handler的全部子類及詳細介紹可以查看官方文檔相應頁面如果需要了解更多關於Handler的信息,直接查看官方文檔即可。

2.1.3 Formatter

Formatter 用於設置日誌輸出的格式,與前兩個基本概念不同的是,該類可以直接初始化對象,即 formatter=logging.Formatter(fmt=None, datefmt=None),創建formatter時,傳入分別fmtdatefmt參數來修改日誌格式和時間格式,默認的日誌格式爲%(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()等方法即可創建日誌內容。

開發者可以通過三種方法配置日誌模塊:

  1. 在Python代碼中顯示創建loggers, handlers, formatters甚至filters,並調用這幾個對象中的各個配置函數來完成日誌配置
  2. 將配置信息寫到配置文件中,然後讀取配置文件信息來完成日誌配置
  3. 將配置信息寫到一個Dict中,然後讀取這個配置字典來完成日誌配置

2.3.1 通過代碼配置並使用日誌模塊

通過代碼配置日誌模塊簡單方便,但如果需要修改配置時,需要改代碼,因此不建議在大型項目中使用這種方法。

通過代碼配置日誌模塊可以很好的理解日誌模塊的工作原理,用於學習,是一個很好的案例,因此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')

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, handlerformatter定義好,然後組裝到一起即可,無非ini配置和代碼配置時的語法不通而已,開發者在基於ini文件配置日誌模塊時,只要參考上面例子做相應修改即可。

2.3.3 通過Dict對象配置並使用日誌模塊

基於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()

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章