Python之日志使用(logging)

一、日志相關(guān)概念

日志是一種可以追蹤某些軟件運(yùn)行時(shí)所發(fā)生事情的方法。軟件開(kāi)發(fā)人員可以向代碼中調(diào)用日志記錄相關(guān)的方法來(lái)表明發(fā)生了某些事情。一個(gè)事件可以用一個(gè)可包含可選變量數(shù)據(jù)的消息來(lái)描述。此外,事件也有重要性的概念,這個(gè)重要性也可以被稱為嚴(yán)重級(jí)別(level).

1.日志的作用

通過(guò)log的分析,可以方便用戶了解系統(tǒng)或軟件、應(yīng)用的運(yùn)行情況;如果你的應(yīng)用log足夠豐富,也可以分析以往用戶的操作行為、類型喜好、地域分布或其他更多信息;如果一個(gè)應(yīng)用的log同時(shí)分了多個(gè)級(jí)別,那么可以輕易地分析得到該應(yīng)用的健康狀況,及時(shí)發(fā)現(xiàn)問(wèn)題并快速定位、解決問(wèn)題,補(bǔ)救損失。
簡(jiǎn)單來(lái)講,我們通過(guò)記錄和分析日志可以了解一個(gè)系統(tǒng)或軟件程序運(yùn)行情況是否正常,也可以在應(yīng)用程序出現(xiàn)故障時(shí)快速定位問(wèn)題。比如,做運(yùn)維的同學(xué),在接收到報(bào)警或各種問(wèn)題反饋后,進(jìn)行問(wèn)題排查時(shí)通常都會(huì)先去看各種日志,大部分問(wèn)題都可以在日志中找到答案。再比如,做開(kāi)發(fā)的童鞋,可以通過(guò)IDE控制臺(tái)上輸出的各種日志進(jìn)行程序調(diào)試。對(duì)于運(yùn)維老司機(jī)或者有經(jīng)驗(yàn)的開(kāi)發(fā)人員,可以快速的通過(guò)日志定位問(wèn)題的根源。可見(jiàn),日志是非常重要的。
日志的作用主要有以下3點(diǎn):

  • 程序調(diào)試
  • 了解軟件運(yùn)行情況是否正常
  • 軟件運(yùn)行故障分析與問(wèn)題定位

如果應(yīng)用的日志信息足夠詳細(xì)和豐富,還可以用來(lái)作用戶行為分析,如:分析用戶的操作行為、類型喜好、地域分布以及其他更多的信息,由此可以實(shí)現(xiàn)改進(jìn)業(yè)務(wù)、提高商業(yè)利益。

2.日志的等級(jí)

我們先來(lái)思考下面的兩個(gè)問(wèn)題:

  • 作為開(kāi)發(fā)人員,在開(kāi)發(fā)一個(gè)應(yīng)用程序時(shí)需要什么日志信息?在應(yīng)用程序正式上線后需要什么日志信息?
  • 作為應(yīng)用運(yùn)維人員,在部署開(kāi)發(fā)環(huán)境時(shí)需要什么日志信息?在部署生產(chǎn)環(huán)境時(shí)需要什么日志信息?

在軟件開(kāi)發(fā)階段或部署開(kāi)發(fā)環(huán)境時(shí),為了盡可能詳細(xì)的查看應(yīng)用程序的運(yùn)行狀態(tài)來(lái)保證上線后的穩(wěn)定性,我們可能需要把該應(yīng)用程序所有的運(yùn)行日志全部記錄下來(lái)進(jìn)行分析,這是非常耗費(fèi)機(jī)器性能的。當(dāng)應(yīng)用程序正式發(fā)布或在生產(chǎn)環(huán)境部署應(yīng)用程序時(shí),我們通過(guò)只需要記錄應(yīng)用程序的異常信息、錯(cuò)誤信息等,這樣既可以減小服務(wù)器的I/O壓力,也可以避免我們?cè)谂挪楣收蠒r(shí)淹沒(méi)在日志的海洋里。那么,怎樣才能在不改動(dòng)應(yīng)用程序代碼的情況下實(shí)現(xiàn)在不同的環(huán)境記錄不同詳細(xì)程度的日志呢?這就是日志等級(jí)的作用了,我們通過(guò)配置文件指定我們需要的日志等級(jí)就可以了。

不同的應(yīng)用程序所定義的日志等級(jí)可能會(huì)有所差別,分的詳細(xì)點(diǎn)的會(huì)包含以下幾個(gè)等級(jí):

  • DEBUG
  • INFO
  • NOTICE
  • WARNING
  • ERROR
  • CRITICAL
  • ALERT
  • EMERGENCY

3.日志字段信息與日志格式

本節(jié)開(kāi)始問(wèn)題提到過(guò),一條日志信息對(duì)應(yīng)的是一個(gè)事件的發(fā)生,而一個(gè)事件通常包括以下幾個(gè)內(nèi)容:

  • 事件發(fā)生時(shí)間
  • 事件發(fā)生位置
  • 事件的嚴(yán)重程序--日志級(jí)別
  • 事件內(nèi)容

上面這些都是一條日志記錄中可能包含的字段信息,當(dāng)然還可以包括一些其他信息,如進(jìn)程ID、進(jìn)程名稱、線程ID、線程名稱等。日志格式就是用來(lái)定義一條日志記錄中包含哪些字段的,且日志格式通常都是可以自定義的。
說(shuō)明:
輸出一條日志時(shí),日志內(nèi)容和日志級(jí)別是需要開(kāi)發(fā)人員明確指定的。對(duì)于其他字段信息,只需要是否顯示在日志中就可以了。

二、logging模塊簡(jiǎn)介

logging模塊定義的函數(shù)和類為應(yīng)用程序和庫(kù)的開(kāi)發(fā)實(shí)現(xiàn)了一個(gè)靈活的事件日志系統(tǒng)。logging模塊是python的一個(gè)標(biāo)準(zhǔn)庫(kù)模塊,由標(biāo)準(zhǔn)庫(kù)模塊提供日志記錄API的關(guān)鍵好處是所有python模塊都可以使用這個(gè)日志記錄功能。所有,你的應(yīng)用日志可以將你自己的日志信息與來(lái)自第三方模塊的信息整合起來(lái)。

1.logging模塊的日志級(jí)別

logging模塊默認(rèn)定義了以下幾個(gè)日志等級(jí),它允許開(kāi)發(fā)人員自定義其他日志級(jí)別,但是這是不被推薦的,尤其是在開(kāi)發(fā)供別人使用的庫(kù)時(shí),因?yàn)檫@會(huì)導(dǎo)致日志級(jí)別的混亂。
日志等級(jí)(level)描述
DEBUG:最詳細(xì)的日志信息,典型應(yīng)用場(chǎng)景是問(wèn)題診斷
INFO:信息詳細(xì)程度僅次于DEBUG,通常只記錄關(guān)鍵節(jié)點(diǎn)信息,用于確認(rèn)一切都是按照我們預(yù)期的那樣進(jìn)行工作
WARNING:當(dāng)某些不期望的事情發(fā)生時(shí)記錄的信息(如,磁盤(pán)可用空間較低),但是此時(shí)應(yīng)用程序還是正常運(yùn)行的
ERROR:由于一個(gè)更嚴(yán)重的問(wèn)題導(dǎo)致某些功能不能正常運(yùn)行時(shí)記錄的信息
CRITICAL:當(dāng)發(fā)生嚴(yán)重錯(cuò)誤,導(dǎo)致應(yīng)用程序不能繼續(xù)運(yùn)行時(shí)繼續(xù)的信息
開(kāi)發(fā)應(yīng)用程序或部署開(kāi)發(fā)環(huán)境時(shí),可以使用DEBUGINFO級(jí)別的日志獲取盡可能詳細(xì)的日志信息來(lái)進(jìn)行開(kāi)發(fā)或部署調(diào)試;應(yīng)用上線或部署生產(chǎn)環(huán)境時(shí),應(yīng)該使用WARNING或ERROR或CRITICAL級(jí)別的日志來(lái)降低機(jī)器的I/O壓力和提高獲取錯(cuò)誤日志信息的效率。日志級(jí)別的指定通常都是在應(yīng)用程序的配置文件中進(jìn)行指定的。
說(shuō)明:

  • 上面列表中的日志等級(jí)是從上到下依次升高的,即:DEBUG<INFO<WARNING<ERROR<CRITICAL,而日志的信息量是依次減少的;
  • 當(dāng)為某個(gè)應(yīng)用程序指定一個(gè)日志級(jí)別后,應(yīng)用程序會(huì)記錄所有日志級(jí)別大于或等于指定日志級(jí)別的日志信息,而不僅僅記錄指定級(jí)別的日志信息,nginx、php等應(yīng)用程序以及這里要提高的python的logging模塊都是這樣的。通過(guò)logging模塊也可以指定日志記錄器的日志級(jí)別,只有級(jí)別大于或等于該指定日志級(jí)別的日志記錄才會(huì)被輸出,小于該等級(jí)的日志記錄將會(huì)被丟棄。

2.logging模塊的使用方式介紹

logging模塊提供了兩種記錄日志的方式:

  • 第一種方式是使用logging提供的模塊級(jí)別的函數(shù)
  • 第二種方式是使用logging日志系統(tǒng)的四大組件
    其實(shí),logging所提供的模塊級(jí)別的日志記錄函數(shù)也是對(duì)logging日志系統(tǒng)相關(guān)類的封裝而已。
logging模塊定義的模塊級(jí)別的常用函數(shù)

函數(shù)及說(shuō)明
logging.debug(msg,args,kwargs):創(chuàng)建一條嚴(yán)重級(jí)別為DEBUG的日志記錄
logging.info(msg, args, kwargs):創(chuàng)建一條嚴(yán)重級(jí)別為INFO的日志記錄
logging.warning(msg,args,kwargs):創(chuàng)建一條嚴(yán)重級(jí)別為WARNING的日志記錄
logging.error(msg,args,kwargs):創(chuàng)建一條嚴(yán)重級(jí)別為ERROR的日志記錄
logging.critical(msg,args,kwargs):創(chuàng)建一條嚴(yán)重級(jí)別為CRITICAL的日志記錄
logging.log(level,args,kwargs):創(chuàng)建一條嚴(yán)重級(jí)別為level的日志記錄
logging.basicConfig(kwargs):對(duì)root logger進(jìn)行一次性配置
其中logging.basicConfig(**kwargs)函數(shù)用于指定“要記錄的日志級(jí)別”、“日志格式”、“日志輸出位置”、“日志文件的打開(kāi)模式”等信息,其他幾個(gè)都是用于記錄各個(gè)級(jí)別日志的函數(shù)。

logging模塊的四大組件

組件及說(shuō)明
loggers:提供應(yīng)用程序代碼直接使用的接口
handlers:用于將日志記錄發(fā)送到指定的目的位置
filters:提供更細(xì)粒度的日志過(guò)濾功能,用于決定哪些日志記錄將會(huì)被輸出(其他的日志記錄將會(huì)被忽略)
formatters:用于控制日志信息的最終輸出格式
說(shuō)明: logging模塊提供的模塊級(jí)別的那些函數(shù)實(shí)際上也是通過(guò)這幾個(gè)組件的相關(guān)實(shí)現(xiàn)類來(lái)記錄日志的,只是在創(chuàng)建這些類的實(shí)例時(shí)設(shè)置了一些默認(rèn)值。

三、使用logging提供的模塊級(jí)別的函數(shù)記錄日志

回顧下前面提到的幾個(gè)重要信息:

  • 可以通過(guò)logging模塊定義的模塊級(jí)別的方法去完成簡(jiǎn)單的日志記錄
  • 只有級(jí)別大于或等于日志記錄器指定級(jí)別的日志記錄才會(huì)被輸出,小于該級(jí)別的日志記錄將會(huì)被丟棄。

1.最簡(jiǎn)單的日志輸出

先來(lái)試著分別輸出一條不同日志級(jí)別的日志記錄:

import logging

logging.debug("This is a debug log.")
logging.info("This is a info log.")
logging.warning("This is a warning log.")
logging.error("This is a error log.")
logging.critical("This is a critical log.")

也可以這樣寫(xiě):

logging.log(logging.DEBUG, "This is a debug log.")
logging.log(logging.INFO, "This is a info log.")
logging.log(logging.WARNING, "This is a warning log.")
logging.log(logging.ERROR, "This is a error log.")
logging.log(logging.CRITICAL, "This is a critical log.")

輸出結(jié)果:

WARNING:root:This is a warning log.
ERROR:root:This is a error log.
CRITICAL:root:This is a critical log.

2.那么問(wèn)題來(lái)了

問(wèn)題1:為什么前面兩條日志沒(méi)有被打印出來(lái)?

這是因?yàn)閘ogging模塊提供的日志記錄函數(shù)所使用的日志器設(shè)置的日志級(jí)別是WARNING,因此只有WARNING級(jí)別的日志記錄以及大于它的級(jí)別的日志記錄被輸出了,而小于他的則被丟棄了。

問(wèn)題2:打印出來(lái)的日志信息中各字段表示什么意思?為什么會(huì)這樣輸出?

上面輸出結(jié)果中每行日志記錄的各個(gè)字段含義分別是:
日志級(jí)別:日志器名稱:日志內(nèi)容
之所以這樣輸出,是因?yàn)?strong>logging模塊提供的日志記錄函數(shù)所使用的日志器設(shè)置的日志格式默認(rèn)是BASIC_FORMAT,其值為:
''%(levelname)s:%(name)s:%(message)s"

問(wèn)題3:如何將日志記錄輸出到文件中,而不是打印到控制臺(tái)

因?yàn)樵?strong>logging模塊提供的日志記錄函數(shù)所使用的日志器設(shè)置的處理器所指定的日志輸出位置默認(rèn)為:
sys.stderr

問(wèn)題4:我是怎么知道這些的?

查看這些日志記錄函數(shù)的實(shí)現(xiàn)代碼,可以發(fā)現(xiàn):當(dāng)我們沒(méi)有提供任何配置信息的時(shí)候,這些函數(shù)都會(huì)去調(diào)用logging.basicConfig(kwargs)方法,且不會(huì)向該方法傳遞任何參數(shù)。繼續(xù)查看basicConfig()方法的代碼就可以找到上面這些問(wèn)題的答案了。

問(wèn)題5:怎么修改這些默認(rèn)設(shè)置呢?

在我們調(diào)用這些日志記錄函數(shù)之前,手動(dòng)調(diào)用一下basicConfig()方法,把我們想設(shè)置的內(nèi)容以參數(shù)的形式傳遞進(jìn)去就可以了。

3.logging.basicConfig()函數(shù)說(shuō)明

該方法用于為logging日志系統(tǒng)做一些基本配置,方法定義如下:
logging.basicConfig(**kwargs)
該函數(shù)可接收的關(guān)鍵字參數(shù)如下:
filename:指定日志輸出目標(biāo)文件的文件名,指定該設(shè)置項(xiàng)后日志信息就不會(huì)被輸出到控制臺(tái)了
filemode:指定日志文件的打開(kāi)模式,默認(rèn)為a,需要注意的是,該選項(xiàng)要在filename指定時(shí)才有效
format:指定日志格式字符串,即指定日志輸出時(shí)所包含的字段信息以及他們的順序。logging模塊定義的格式字段下面會(huì)列出。
datefmt:指定日期/時(shí)間格式。需要注意的是,該選項(xiàng)要在format中包含時(shí)間字段%(asctime)s時(shí)才有效
level:指定日志器的日志級(jí)別
stream:指定日志輸出目標(biāo)stream,如sys.stdoutsys.stderr以及網(wǎng)絡(luò)stream。需要說(shuō)明的是,streamfilename不能同時(shí)提供,否則會(huì)引發(fā)ValueError異常
style:python3中新添加的配置項(xiàng)。指定format格式字符串的風(fēng)格,可取%{$,默認(rèn)為%
handlers:python3中新添加的配置項(xiàng)。該選項(xiàng)如果被指定,它應(yīng)該是一個(gè)創(chuàng)建了多個(gè)
handler的可迭代對(duì)象,這些handler將會(huì)被添加到root logger*。需要說(shuō)明的是:filename、stream和handlers這三個(gè)配置項(xiàng)只能有一個(gè)存在,不能同時(shí)出現(xiàn)兩個(gè)以上,否則會(huì)引發(fā)ValueError異常。

4.logging模塊定義的格式字符串字段

我們來(lái)列舉一下logging模塊中定義好的可以用于format格式字符串中字段有哪些:
字段/屬性名稱-使用格式-描述
asctime:%(asctime)s:日志事件發(fā)生的時(shí)間--人類可讀時(shí)間,如:2003-07-08 16:59:59,896
created:%(created)f:日志事件發(fā)生的時(shí)間--時(shí)間戳,就是當(dāng)時(shí)調(diào)用time.time()函數(shù)返回的值
relativeCreated: %(relatedCreated)d:日志事件發(fā)生的時(shí)間相對(duì)于logging模塊加載時(shí)間的相對(duì)毫秒數(shù)(目前還不知道干嘛用的)
msecs: %(msecs)d:日志事件發(fā)生事件的毫秒部分
levelname: %(levelname)s:該日志記錄的文字形式的日志級(jí)別('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL')
levelno: %(levelno)s:該日志記錄的數(shù)字形式的日志級(jí)別(10,20,30,40,50)
name: %(name)s:所使用的日志器名稱,默認(rèn)是root,因?yàn)槟J(rèn)使用的是rootLogger
message: %(message)s:日志記錄的文本內(nèi)容,通過(guò)msg % args計(jì)算得到的
pathname: %(pathname)s:調(diào)用日志記錄函數(shù)的源碼文件的全路徑
filename: %(filename)s:pathname的文件名部分,包含文件后綴
module: %(module)s:filename的名稱部分,不包含后綴
lineno: %(lineno)d:調(diào)用日志記錄函數(shù)的源代碼所在的行號(hào)
funcName: %(funcName)s:調(diào)用日志記錄函數(shù)的函數(shù)名
process: %(process)d:進(jìn)程ID
processName: %(processName)s:進(jìn)程名稱,python3.1新增
thread:%(thread)d:線程ID
threadName: %(threadName)s:線程名稱

5.經(jīng)過(guò)配置的日志輸出

先簡(jiǎn)單配置下日志器的日志級(jí)別

logging.basicConfig(level=logging.DEBUG)

logging.debug('This is a debug log.')
logging.info("This is a info log.")
logging.warning("This is a warning log.")
logging.error("This is a error log.")
logging.critical("This is a critical log.")

輸出結(jié)果:

DEBUG:root:This is a debug log.
INFO:root:This is a info log.
WARNING:root:This is a warning log.
ERROR:root:This is a error log.
CRITICAL:root:This is a critical log.

所有等級(jí)的日志信息都被輸出了,說(shuō)明配置生效了。
在配置日志器日志級(jí)別的基礎(chǔ)上,再配置下日志輸出目標(biāo)文件和日志格式

LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s"
logging.basicConfig(filename='my.log', level=logging.DEBUG, format=LOG_FORMAT)

logging.debug("This is a debug log.")
logging.info("This is a info log.")
logging.warning("This is a warning log.")
logging.error("This is a error log.")
logging.critical("This is a critical log.")

此時(shí)會(huì)發(fā)現(xiàn)控制臺(tái)中已經(jīng)沒(méi)有輸出日志內(nèi)容了,但是在python代碼文件的相同目錄下回生成一個(gè)名為'my.log'的日志文件,該文件中的內(nèi)容為:

2017-05-08 14:29:53,783 - DEBUG - This is a debug log.
2017-05-08 14:29:53,784 - INFO - This is a info log.
2017-05-08 14:29:53,784 - WARNING - This is a warning log.
2017-05-08 14:29:53,784 - ERROR - This is a error log.
2017-05-08 14:29:53,784 - CRITICAL - This is a critical log.

在上面的基礎(chǔ)上,我們?cè)賮?lái)設(shè)置下日期/時(shí)間格式

LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s"
DATE_FORMAT = "%m/%d/%Y %H:%M:%S %p"

logging.basicConfig(filename='my.log', level=logging.DEBUG, format=LOG_FORMAT, datefmt=DATE_FORMAT)

logging.debug("This is a debug log.")
logging.info("This is a info log.")
logging.warning("This is a warning log.")
logging.error("This is a error log.")
logging.critical("This is a critical log.")

此時(shí)會(huì)在my.log日志文件中看到如下輸出內(nèi)容:

05/08/2017 14:29:04 PM - DEBUG - This is a debug log.
05/08/2017 14:29:04 PM - INFO - This is a info log.
05/08/2017 14:29:04 PM - WARNING - This is a warning log.
05/08/2017 14:29:04 PM - ERROR - This is a error log.
05/08/2017 14:29:04 PM - CRITICAL - This is a critical log.

掌握了上面的內(nèi)容之后,已經(jīng)能夠滿足我們平時(shí)開(kāi)發(fā)中需要的日志記錄功能。

6.其他說(shuō)明

幾個(gè)要說(shuō)明的內(nèi)容:

  • logging.basicConfig()函數(shù)是一個(gè)一次性的簡(jiǎn)單配置工具,也就是說(shuō)只有在第一次調(diào)用該函數(shù)時(shí)會(huì)起作用,后續(xù)再次調(diào)用該函數(shù)時(shí)完全不會(huì)產(chǎn)生任何操作的,多次調(diào)用的設(shè)置并不是累加操作。
  • 日志器(Logger)是有層級(jí)關(guān)系的,上面調(diào)用的logging模塊級(jí)別的函數(shù)所使用的日志器是RootLogger類的實(shí)例,其名稱為root,它是處于日志器層級(jí)關(guān)系最頂層的日志器,且該實(shí)例是以單例模式存在的。
  • 如果要記錄的日志中包含變量數(shù)據(jù),可使用一個(gè)格式字符串作為這個(gè)時(shí)間的描述消息(logging.debug、logging.info等函數(shù)的第一個(gè)參數(shù)),然后將變量數(shù)據(jù)作為第二個(gè)參數(shù)*args的值進(jìn)行傳遞,如:
    logging.warning('%s is %d years old.', 'Tom', 10),輸出內(nèi)容為:WARNING:root:Tom is 10 years old.
  • logging.debug()、logging.info()等方法的定義中,除了msg和args參數(shù)外,還有一個(gè)**kwargs參數(shù)。它們支持3個(gè)關(guān)鍵字參數(shù):
    exc_info, stack_info, extra,下面對(duì)這幾個(gè)關(guān)鍵字參數(shù)作個(gè)說(shuō)明。
    關(guān)于exc_info,stack_info,extra關(guān)鍵詞參數(shù)的說(shuō)明:
  • exc_info:其值為布爾值,如果該參數(shù)的值設(shè)置為T(mén)rue,則會(huì)將異常信息添加到日志消息中。如果沒(méi)有異常信息則添加NONE到日志信息中。
  • stack_info:其值也為布爾值,默認(rèn)值為False。如果該參數(shù)的值設(shè)置為T(mén)rue,棧信息將會(huì)被添加到日志信息中。
  • extra:這是一個(gè)字典參數(shù),它可以用來(lái)自定義消息格式中所包含的字段,但是它的key不能與logging模塊定義的字段沖突。
    一個(gè)例子:
    在日志消息中添加exc_infostack_info信息,并添加兩個(gè)自定義的字段IP和user
LOG_FORMAT = "%(asctime)s - %(levelname)s - %(user)s[%(ip)s] - %(message)s"
DATE_FORMAT = "%m/%d/%Y %H:%M:%S %p"

logging.basicConfig(format=LOG_FORMAT, datefmt=DATE_FORMAT)
logging.warning("Some one delete the log file.", exc_info=True, stack_info=True, extra={'user': 'Tom', 'ip':'47.98.53.222'})

輸出結(jié)果:

05/08/2017 16:35:00 PM - WARNING - Tom[47.98.53.222] - Some one delete the log file.
NoneType
Stack (most recent call last):
  File "C:/Users/wader/PycharmProjects/LearnPython/day06/log.py", line 45, in <module>
    logging.warning("Some one delete the log file.", exc_info=True, stack_info=True, extra={'user': 'Tom', 'ip':'47.98.53.222'})

四、logging模塊日志流處理流程

在介紹logging模塊的高級(jí)用法之前,很有必要對(duì)logging模塊所包含的重要組件以及其工作流程做個(gè)全面、簡(jiǎn)要的介紹,這有助于我們更好的理解我們所寫(xiě)的代碼(將會(huì)觸發(fā)什么樣的操作)。

1.logging日志模塊四大組件

在介紹logging模塊的日志流處理之前,我們先來(lái)介紹下logging模塊的四大組件:
日志器 Logger:提供了應(yīng)用程序可一直使用的接口
處理器 Handler:將logger創(chuàng)建的日志記錄發(fā)送到合適的目的輸出
過(guò)濾器 Filter:提供了更細(xì)粒度的控制工具來(lái)決定輸出哪條日志記錄,丟棄哪條日志記錄
格式器 Formatter:決定日志記錄的最終輸出格式

logging模塊就是通過(guò)這些組件來(lái)完成日志處理的,上面所使用的logging模塊級(jí)別的函數(shù)也是通過(guò)這些組件對(duì)應(yīng)的類來(lái)實(shí)現(xiàn)的。
這些組件之間的關(guān)系描述:

  • 日志器(logger)需要通過(guò)處理器(handler)將日志信息輸出到目標(biāo)位置,如文件、sys.stdout、網(wǎng)絡(luò)等;
  • 不同的處理器(handler)可以將日志輸出到不同的位置;
  • 日志器(logger)可以設(shè)置多個(gè)處理器(handler)將同一條日志記錄輸出到不同的位置;
  • 每個(gè)處理器(handler)都可以設(shè)置自己的過(guò)濾器(filter)實(shí)現(xiàn)日志過(guò)濾,從而只保留感興趣的日志;
  • 每個(gè)處理器(handler)都可以設(shè)置自己的格式器(formatter)實(shí)現(xiàn)同一條日志以不同的格式輸出到不同的地方。

簡(jiǎn)單點(diǎn)說(shuō)就是:日志器(logger)是入口,真正干活兒的是處理器(handler),處理器(handler)還可以通過(guò)過(guò)濾器(filter)和格式器(formatter)對(duì)要輸出的日志內(nèi)容做過(guò)濾和格式化等處理操作。

2.logging日志模塊相關(guān)類及其常用方法介紹

下面介紹與logging四大組件相關(guān)的類:Logger,Handler,F(xiàn)ilter,F(xiàn)ormatter
Logger
Logger對(duì)象有3個(gè)任務(wù)要做:

  • 1.向應(yīng)用程序代碼暴露幾個(gè)方法,使應(yīng)用程序可以在運(yùn)行時(shí)記錄日志消息;
  • 2.基于日志嚴(yán)重等級(jí)(默認(rèn)的過(guò)濾設(shè)施)或filter對(duì)象來(lái)決定要對(duì)哪些日志進(jìn)行后續(xù)處理;
  • 3.將日志消息傳送給所有感興趣的日志handlers
    Logger對(duì)象最常用的方法分為兩類:配置方法和消息發(fā)送方法
    最常用的配置方法如下:
    Logger.setLevel():設(shè)置日志器將會(huì)處理的日志消息的最低嚴(yán)重級(jí)別
    Logger.addHandler()/Logger.removeHandler():為該logger對(duì)象添加和移除一個(gè)handler對(duì)象
    Logger.addFilter()/Logger.removeFilter():為該logger對(duì)象添加和移除一個(gè)filter對(duì)象
    關(guān)于Logger.setLevel()方法的說(shuō)明:
    內(nèi)建等級(jí)中,級(jí)別最低的是DEBUG,級(jí)別最高的是CRITICAL。例如setLevel(logging.INFO),此時(shí)函數(shù)參數(shù)為INFO,那么該logger將只會(huì)處理INFO、WARNING、ERROR和CRITICAL級(jí)別的日志,而DEBUG級(jí)別的消息將會(huì)被忽略/丟棄。
    logger對(duì)象配置完成后,可以使用下面的方法來(lái)創(chuàng)建日志記錄:
    Logger.debug(), Logger.info()......Logger.critical():創(chuàng)建一個(gè)與它們的方法名對(duì)應(yīng)等級(jí)的日志記錄
    Logger.exception():創(chuàng)建一個(gè)類似于Logger.error()的日志消息
    Logger.log():需要獲取一個(gè)明確的日志level參數(shù)來(lái)創(chuàng)建一個(gè)日志記錄
    說(shuō)明:
  • Logger.exception()與Logger.error()的區(qū)別在于:Logger.exception()將會(huì)輸出堆棧追蹤信息,另外通常只是在一個(gè)exception handler中調(diào)用該方法。
  • Logger.log()與Logger.debug() Logger.info()等方法相比,雖然需要多傳一個(gè)level參數(shù),顯得不是那么方便,但是當(dāng)需要記錄自定義level的日志時(shí)還是需要該方法來(lái)完成。

那么,怎樣得到一個(gè)Logger對(duì)象呢?一種方式是通過(guò)Logger類的實(shí)例化方法創(chuàng)建一個(gè)Logger類的實(shí)例,但是我們通常都是使用第二種方式--logging.getLogger()方法。
logging.getLogger()方法有一個(gè)可選參數(shù)name,該參數(shù)表示將要返回的日志器的名稱標(biāo)識(shí),如果不提供該參數(shù),則其值為root。若以相同的name參數(shù)值多次調(diào)用getLogger()方法,將會(huì)返回指向同一個(gè)logger對(duì)象的引用。
關(guān)于logger的層級(jí)結(jié)構(gòu)與有效等級(jí)的說(shuō)明:

  • logger的名稱是一個(gè)以'.'分割的層級(jí)結(jié)構(gòu),每個(gè)'.'后面的logger都是'.'前面的logger的children,例如,有一個(gè)名稱為 foo 的logger,其它名稱分別為 foo.bar, foo.bar.baz 和 foo.bam都是 foo 的后代。
  • logger有一個(gè)"有效等級(jí)(effective level)"的概念。如果一個(gè)logger上沒(méi)有被明確設(shè)置一個(gè)level,那么該logger就是使用它parent的level;如果它的parent也沒(méi)有明確設(shè)置level則繼續(xù)向上查找parent的parent的有效level,依次類推,直到找到個(gè)一個(gè)明確設(shè)置了level的祖先為止。需要說(shuō)明的是,root logger總是會(huì)有一個(gè)明確的level設(shè)置(默認(rèn)為 WARNING)。當(dāng)決定是否去處理一個(gè)已發(fā)生的事件時(shí),logger的有效等級(jí)將會(huì)被用來(lái)決定是否將該事件傳遞給該logger的handlers進(jìn)行處理。
  • child loggers在完成對(duì)日志消息的處理后,默認(rèn)會(huì)將日志消息傳遞給與它們的祖先loggers相關(guān)的handlers。因此,我們不必為一個(gè)應(yīng)用程序中所使用的所有l(wèi)oggers定義和配置handlers,只需要為一個(gè)頂層的logger配置handlers,然后按照需要?jiǎng)?chuàng)建child loggers就可足夠了。我們也可以通過(guò)將一個(gè)logger的propagate屬性設(shè)置為False來(lái)關(guān)閉這種傳遞機(jī)制。
    Handler類
    Handler對(duì)象的作用是(基于日志消息的level)將消息分發(fā)到handler指定的位置(文件、網(wǎng)絡(luò)、郵件等)。Logger對(duì)象可以通過(guò)addHandler()方法為自己添加0個(gè)或者更多個(gè)handler對(duì)象。比如,一個(gè)應(yīng)用程序可能想要實(shí)現(xiàn)以下幾個(gè)日志需求:
  • 1)把所有日志都發(fā)送到一個(gè)日志文件中;
  • 2)把所有嚴(yán)重級(jí)別大于等于error的日志發(fā)送到stdout(標(biāo)準(zhǔn)輸出);
  • 3)把所有嚴(yán)重級(jí)別為critical的日志發(fā)送到一個(gè)email郵件地址。
    這種場(chǎng)景就需要3個(gè)不同的handlers,每個(gè)handler復(fù)雜發(fā)送一個(gè)特定嚴(yán)重級(jí)別的日志到一個(gè)特定的位置。
    一個(gè)handler中只有非常少數(shù)的方法是需要應(yīng)用開(kāi)發(fā)人員去關(guān)心的。對(duì)于使用內(nèi)建handler對(duì)象的應(yīng)用開(kāi)發(fā)人員來(lái)說(shuō),似乎唯一相關(guān)的handler方法就是下面這幾個(gè)配置方法:
    Handler.setLevel() 設(shè)置handler將會(huì)處理的日志消息的最低嚴(yán)重級(jí)別
    Handler.setFormatter() 為handler設(shè)置一個(gè)格式器對(duì)象
    Handler.addFilter() 和 Handler.removeFilter() 為handler添加 和 刪除一個(gè)過(guò)濾器對(duì)象
    需要說(shuō)明的是,應(yīng)用程序代碼不應(yīng)該直接實(shí)例化和使用Handler實(shí)例。因?yàn)镠andler是一個(gè)基類,它只定義了素有handlers都應(yīng)該有的接口,同時(shí)提供了一些子類可以直接使用或覆蓋的默認(rèn)行為。下面是一些常用的Handler:
    logging.StreamHandler 將日志消息發(fā)送到輸出到Stream,如std.out, std.err或任何file-like對(duì)象。
    logging.FileHandler 將日志消息發(fā)送到磁盤(pán)文件,默認(rèn)情況下文件大小會(huì)無(wú)限增長(zhǎng)
    logging.handlers.RotatingFileHandler 將日志消息發(fā)送到磁盤(pán)文件,并支持日志文件按大小切割
    logging.hanlders.TimedRotatingFileHandler 將日志消息發(fā)送到磁盤(pán)文件,并支持日志文件按時(shí)間切割
    logging.handlers.HTTPHandler 將日志消息以GET或POST的方式發(fā)送給一個(gè)HTTP服務(wù)器
    logging.handlers.SMTPHandler 將日志消息發(fā)送給一個(gè)指定的email地址
    logging.NullHandler 該Handler實(shí)例會(huì)忽略error messages,通常被想使用logging的library開(kāi)發(fā)者使用來(lái)避免'No handlers could be found for logger XXX'信息的出現(xiàn)。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,606評(píng)論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,582評(píng)論 3 418
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 176,540評(píng)論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,028評(píng)論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,801評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,223評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,294評(píng)論 3 442
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,442評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,976評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,800評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,996評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,543評(píng)論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,233評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,662評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,926評(píng)論 1 286
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,702評(píng)論 3 392
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,991評(píng)論 2 374

推薦閱讀更多精彩內(nèi)容