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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,119評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,382評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,038評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,853評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,616評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,112評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,192評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,355評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,869評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,727評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,928評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,467評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,165評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,570評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,813評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,585評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,892評論 2 372

推薦閱讀更多精彩內容