詳解?python?logging日志模塊
轉(zhuǎn)自微信公眾號(hào): Python之禪
1.日志簡(jiǎn)介
說(shuō)到日志,無(wú)論是寫(xiě)框架代碼還是業(yè)務(wù)代碼,都離不開(kāi)日志的記錄,他能給我們定位問(wèn)題帶來(lái)極大的幫助。
記錄日志最簡(jiǎn)單的方法就是在你想要記錄的地方加上一句 print , 我相信無(wú)論是新手還是老鳥(niǎo)都經(jīng)常這么干。在簡(jiǎn)單的代碼中或者小型項(xiàng)目中這么干一點(diǎn)問(wèn)題都沒(méi)有。但是在一些稍大一點(diǎn)的項(xiàng)目,有時(shí)候定位一個(gè)問(wèn)題,需要查看歷史日志定位問(wèn)題,用print就不合時(shí)宜了。
print
打印出來(lái)的日志沒(méi)有時(shí)間,不知道日志記錄的位置,也沒(méi)有可讀的日志格式, 還不能把日志輸出到指定文件。。。。除非這些你都全部自己重復(fù)造一遍輪子。
最佳的做法是使用內(nèi)置的logging模塊, 因?yàn)?logging
模塊給開(kāi)發(fā)者提供了非常豐富的功能。
比如上圖就是用標(biāo)準(zhǔn)庫(kù)logging
模塊記錄生成的日志,有日志的具體時(shí)間、日志發(fā)生的模塊、有日志級(jí)別和日志的具體內(nèi)容等等
怎么用呢,來(lái)看個(gè)例子:
導(dǎo)入logging
模塊,然后直接使用logging提供的日志消息記錄方法就可以。
2.日志級(jí)別
日志級(jí)別分為以下5個(gè)級(jí)別
日志級(jí)別 | 使用場(chǎng)景 |
---|---|
DEBUG | debug級(jí)別用來(lái)記錄詳細(xì)的信息,方便定位問(wèn)題進(jìn)行調(diào)試,在生產(chǎn)環(huán)境我們一般不開(kāi)啟DEBUG |
INFO | 用來(lái)記錄關(guān)鍵代碼點(diǎn)的信息,以便代碼是否按照我們預(yù)期的執(zhí)行,生產(chǎn)環(huán)境通常會(huì)設(shè)置INFO級(jí)別 |
WARNING | 記錄某些不預(yù)期發(fā)生的情況,如磁盤(pá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í)記錄的信息 |
日志級(jí)別重要程度逐次提高,python提供了5個(gè)對(duì)應(yīng)級(jí)別的方法。默認(rèn)情況下日志的級(jí)別是WARGING, 低于WARING
的日志信息都不會(huì)輸出。
從上面代碼中可以看到loging.warging以后的日志內(nèi)容都打印在標(biāo)準(zhǔn)輸出流,也就是命令行窗口,但是logging.debug
和info記錄的日志不會(huì)打印出來(lái)。
3.修改日志級(jí)別
如何讓debug級(jí)別的信息也輸出?
當(dāng)然是修改默認(rèn)的日志級(jí)別,在開(kāi)始記錄日志前可以使用logging.basicConfig
方法來(lái)設(shè)定日志級(jí)別
import logging logging.basicConfig( level=logging.DEBUG) logging.debug("this is debug") logging.info("this is info") logging.error("this is error")
設(shè)置為debug
級(jí)別后,所有的日志信息都會(huì)輸出
DEBUG:root:this is debug INFO:root:this is info ERROR:root:this is error
4.日志記錄到文件
前面的日志默認(rèn)會(huì)把日志輸出到標(biāo)準(zhǔn)輸出流,就是只在命令行窗口輸出,程序重啟后歷史日志沒(méi)地方找,所以把日志內(nèi)容永久記錄是一個(gè)很常見(jiàn)的需求。同樣通過(guò)配置函數(shù)logging.basicConfig
可以指定日志輸出到什么地方
import logging logging.basicConfig(filename="test.log", level=logging.INFO) logging.debug("this is debug") logging.info("this is info") logging.error("this is error")
這里我指定日志輸出到文件test.log
中,日志級(jí)別指定為了 INFO,最后文件中記錄的內(nèi)容如下:
INFO:root:this is info ERROR:root:this is error
每次重新運(yùn)行時(shí),日志會(huì)以追加的方式在后面, 如果每次運(yùn)行前要覆蓋之前的日志,則需指定 filemode='w', 這個(gè)和 open 函數(shù)寫(xiě)數(shù)據(jù)到文件用的參數(shù)是一樣的。
5.指定日志格式
默認(rèn)輸出的格式包含3部分,日志級(jí)別,日志記錄器的名字,以及日志內(nèi)容,中間用“:”連接。如果我們想改變?nèi)罩靖袷?,例如想加入日期時(shí)間、顯示日志器名字,我們是可以指定format
參數(shù)來(lái)設(shè)置日志的格式
import logging logging.basicConfig(format='%(asctime)s %(levelname)s %(name)s %(message)s') logging.error("this is error")
輸出:
2021-12-15 07:44:16,547 ERROR root this is error
日志格式化輸出提供了非常多的參數(shù),除了時(shí)間、日志級(jí)別、日志消息內(nèi)容、日志記錄器的名字外,還可以指定線程名,進(jìn)程名等等
到這里為止,日志模塊的基本用法就這些了,也能滿足大部分應(yīng)用場(chǎng)景,更高級(jí)的方法接著往下看,可以幫助你更好的處理日志
6.記錄器(logger)
前面介紹的日志記錄,其實(shí)都是通過(guò)一個(gè)叫做日志記錄器(Logger
)的實(shí)例對(duì)象創(chuàng)建的,每個(gè)記錄器都有一個(gè)名稱,直接使用logging
來(lái)記錄日志時(shí),系統(tǒng)會(huì)默認(rèn)創(chuàng)建 名為 root 的記錄器,這個(gè)記錄器是根記錄器。記錄器支持層級(jí)結(jié)構(gòu),子記錄器通常不需要單獨(dú)設(shè)置日志級(jí)別以及Handler
(后面會(huì)介紹),如果子記錄器沒(méi)有單獨(dú)設(shè)置,則它的行為會(huì)委托給父級(jí)。
記錄器名稱可以是任意名稱,不過(guò)最佳實(shí)踐是直接用模塊的名稱當(dāng)作記錄器的名字。
命名如下:
logger = logging.getLogger(__name__)
默認(rèn)情況下,記錄器采用層級(jí)結(jié)構(gòu),上句點(diǎn)作為分隔符排列在命名空間的層次結(jié)構(gòu)中。層次結(jié)構(gòu)列表中位于下方的記錄器是列表中較高位置的記錄器的子級(jí)。例如,有個(gè)名叫 foo 的記錄器,而名字是 foo.bar
,foo.bar.baz
,和foo.bam
的記錄器都是 foo 的子級(jí)。
├─foo
│ │ main.py
│ │ __init__.py
│ │
│ ├─bam
│ │ │ __init__.py
│ │ │
│ │
│ ├─bar
│ │ │ __init__.py
│ │ │
│ │ ├─baz
│ │ │ │ __init__.py
│ │ │ │
main.py:
import foo from foo import bar from foo import bam from foo.bar import baz if __name__ == '__main__': ? ? pass
foo.py
import logging logging.basicConfig() logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) logger.info("this is foo")
這里我只設(shè)置foo這個(gè)記錄器的級(jí)別為INFO
bar.py
import logging logger = logging.getLogger(__name__) logger.info("this is bar")
其它子模塊都是像bar.py一樣類似的代碼,都沒(méi)有設(shè)置日志級(jí)別,最后的輸出結(jié)果是
INFO:foo:this is foo INFO:foo.bar:this is bar INFO:foo.bam:this is bam INFO:foo.bar.baz:this is baz
這是因?yàn)?foo.bar 這個(gè)記錄器沒(méi)有設(shè)置日志級(jí)別,就會(huì)向上找到已經(jīng)設(shè)置了日日志級(jí)別的祖先,這里剛好找到父記錄器foo的級(jí)別為INFO,如果foo也沒(méi)設(shè)置的話,就會(huì)找到根記錄器root,root默認(rèn)的級(jí)別為WARGING。
7.處理器(Handler)
記錄器負(fù)責(zé)日志的記錄,但是日志最終記錄在哪里記錄器并不關(guān)心,而是交給了另一個(gè)家伙--處理器(Handler)去處理。
例如一個(gè)Flask項(xiàng)目,你可能會(huì)將INFO級(jí)別的日志記錄到文件,將ERROR級(jí)別的日志記錄到標(biāo)準(zhǔn)輸出,將某些關(guān)鍵日志(例如有訂單或者嚴(yán)重錯(cuò)誤)發(fā)送到某個(gè)郵件地址通知老板。這時(shí)候你的記錄器添加多個(gè)不同的處理器來(lái)處理不同的消息日志,以此根據(jù)消息的重要性發(fā)送的特定的位置。
Python內(nèi)置了很多實(shí)用的處理器,常用的有:
- 1、StreamHandler 標(biāo)準(zhǔn)流處理器,將消息發(fā)送到標(biāo)準(zhǔn)輸出流、錯(cuò)誤流
- 2、FileHandler 文件處理器,將消息發(fā)送到文件
- 3、RotatingFileHandler 文件處理器,文件達(dá)到指定大小后,啟用新文件存儲(chǔ)日志
- 4、TimedRotatingFileHandler 文件處理器,日志以特定的時(shí)間間隔輪換日志文件
8.處理器操作
Handler
提供了4個(gè)方法給開(kāi)發(fā)者使用,細(xì)心的你可以發(fā)現(xiàn)了,logger可以設(shè)置level,Handler也可以設(shè)置Level。通過(guò)setLevel可以將記錄器記錄的不同級(jí)別的消息發(fā)送到不同的地方去。
import logging from logging import StreamHandler from logging import FileHandler logger = logging.getLogger(__name__) # 設(shè)置為DEBUG級(jí)別 logger.setLevel(logging.DEBUG) # 標(biāo)準(zhǔn)流處理器,設(shè)置的級(jí)別為WARAING stream_handler = StreamHandler() stream_handler.setLevel(logging.WARNING) logger.addHandler(stream_handler) # 文件處理器,設(shè)置的級(jí)別為INFO file_handler = FileHandler(filename="test.log") file_handler.setLevel(logging.INFO) logger.addHandler(file_handler) logger.debug("this is debug") logger.info("this is info") logger.error("this is error") logger.warning("this is warning")
運(yùn)行后,在命令行窗口輸出的日志內(nèi)容是:
this is error this is warning
輸出在文件的日志內(nèi)容是:
this is info this is error this is warning
盡管我們將logger的級(jí)別設(shè)置為了DEBUG,但是debug記錄的消息并沒(méi)有輸出,因?yàn)槲医o兩個(gè)Handler設(shè)置的級(jí)別都比DEBUG要高,所以這條消息被過(guò)濾掉了。
9.格式器(formatter)
格式器在文章的前面部分其實(shí)已經(jīng)有所介紹,不過(guò)那是通過(guò)logging.basicConfig來(lái)指定的,其實(shí)格式器還可以以對(duì)象的形式來(lái)設(shè)置在Handler上。格式器可以指定日志的輸出格式,要不要展示時(shí)間,時(shí)間格式什么,要不要展示日志的級(jí)別,要不要展示記錄器的名字等等,都可以通過(guò)一個(gè)格式器對(duì)消息進(jìn)行格式化輸出。
import logging from logging import StreamHandler logger = logging.getLogger(__name__) # 標(biāo)準(zhǔn)流處理器 stream_handler = StreamHandler() stream_handler.setLevel(logging.WARNING) # 創(chuàng)建一個(gè)格式器 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') # 作用在handler上 stream_handler.setFormatter(formatter) # 添加處理器 logger.addHandler(stream_handler) logger.info("this is info") logger.error("this is error") logger.warning("this is warning")
注意:格式器只能作用在處理器上,通過(guò)處理器的setFromatter方法設(shè)置格式器。而且一個(gè)Handler只能設(shè)置一個(gè)格式器。是一對(duì)一的關(guān)系。而 logger 與 handler 是一對(duì)多的關(guān)系,一個(gè)logger可以添加多個(gè)handler。handler 和 logger 都可以設(shè)置日志的等級(jí)。
10.logging.basicConfig
回到最開(kāi)始的地方,logging.basicConfig()
方法為我們干了啥?現(xiàn)在你大概能猜出來(lái)了。來(lái)看python源碼中是怎么說(shuō)的
Do basic configuration for the logging system.
This function does nothing if the root logger already has handlers configured. It is a convenience method intended for use by simple scripts to do one-shot configuration of the logging package.
The default behaviour is to create a StreamHandler which writes to sys.stderr, set a formatter using the BASIC_FORMAT format string, and add the handler to the root logger.
A number of optional keyword arguments may be specified, which can alter the default behaviour.
- 1、創(chuàng)建一個(gè)root記錄器
- 2、設(shè)置root的日志級(jí)別為warning
- 3、為root記錄器添加StreamHandler處理器
- 4、為處理器設(shè)置一個(gè)簡(jiǎn)單格式器
logging.basicConfig() logging.warning("hello")
這兩行代碼其實(shí)就等價(jià)于:
import sys import logging from logging import StreamHandler from logging import Formatter logger = logging.getLogger("root") logger.setLevel(logging.WARNING) handler = StreamHandler(sys.stderr) logger.addHandler(handler) formatter = Formatter(" %(levelname)s:%(name)s:%(message)s") handler.setFormatter(formatter) logger.warning("hello")
logging.basicConfig 方法做的事情是相當(dāng)于給日志系統(tǒng)做一個(gè)最基本的配置,方便開(kāi)發(fā)者快速接入使用。它必須在開(kāi)始記錄日志前調(diào)用。不過(guò)如果 root 記錄器已經(jīng)指定有其它處理器,這時(shí)候你再調(diào)用basciConfig,則該方式將失效,它什么都不做。
11.日志配置
日志的配置除了前面介紹的將配置直接寫(xiě)在代碼中,還可以將配置信息單獨(dú)放在配置文件中,實(shí)現(xiàn)配置與代碼分離。
日志配置文件 logging.conf
[loggers] keys=root [handlers] keys=consoleHandler [formatters] keys=simpleFormatter [logger_root] level=DEBUG handlers=consoleHandler [handler_consoleHandler] class=StreamHandler level=DEBUG formatter=simpleFormatter args=(sys.stdout,) [formatter_simpleFormatter] format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
加載配置文件
import logging import logging.config # 加載配置 logging.config.fileConfig('logging.conf') # 創(chuàng)建 logger logger = logging.getLogger() # 應(yīng)用代碼 logger.debug("debug message") logger.info("info message") logger.warning("warning message") logger.error("error message")
輸出:
2021-12-23 00:02:07,019 - root - DEBUG - debug message
2021-12-23 00:02:07,019 - root - INFO - info message
2021-12-23 00:02:07,019 - root - WARNING - warning message
2021-12-23 00:02:07,019 - root - ERROR - error message
到這里算是對(duì)logging
的一次比較完整的介紹,當(dāng)然,還有很多細(xì)節(jié)并沒(méi)有涉及到,因此我給了幾個(gè)鏈接供參考。
到此這篇關(guān)于詳解 python logging日志模塊的文章就介紹到這了,更多相關(guān)python logging日志模塊內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python獲取標(biāo)準(zhǔn)北京時(shí)間的方法
這篇文章主要介紹了python獲取標(biāo)準(zhǔn)北京時(shí)間的方法,實(shí)例分析了Python通過(guò)www.beijing-time.org的官網(wǎng)獲取標(biāo)準(zhǔn)北京時(shí)間的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-03-0310分鐘用python搭建一個(gè)超好用的CMDB系統(tǒng)
這篇文章主要介紹了10分鐘用python搭建一個(gè)超好用的CMDB系統(tǒng),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07python+selenium select下拉選擇框定位處理方法
今天小編就為大家分享一篇python+selenium select下拉選擇框定位處理方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-08-08舉例講解Django中數(shù)據(jù)模型訪問(wèn)外鍵值的方法
這篇文章主要介紹了舉例講解Django中數(shù)據(jù)模型訪問(wèn)外鍵值的方法,Django是最具人氣的Python web開(kāi)發(fā)框架,需要的朋友可以參考下2015-07-07Python函數(shù)的默認(rèn)參數(shù)設(shè)計(jì)示例詳解
這篇文章主要給大家介紹了關(guān)于Python函數(shù)的默認(rèn)參數(shù)設(shè)計(jì)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Python具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12Django如何實(shí)現(xiàn)密碼錯(cuò)誤報(bào)錯(cuò)提醒
這篇文章主要介紹了Django如何實(shí)現(xiàn)密碼錯(cuò)誤報(bào)錯(cuò)提醒,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值2020-09-09Python requests發(fā)送post請(qǐng)求的一些疑點(diǎn)
在Python爬蟲(chóng)中,使用requests發(fā)送請(qǐng)求,訪問(wèn)指定網(wǎng)站,是常見(jiàn)的做法,這篇文章主要介紹了Python requests發(fā)送post請(qǐng)求的一些疑點(diǎn),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05詳解Python常用標(biāo)準(zhǔn)庫(kù)之os模塊與shutil模塊
os系統(tǒng)模塊與shutil文件操作模塊是Python常用的標(biāo)準(zhǔn)庫(kù),本文將通過(guò)示例詳細(xì)講解一下二者的使用,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2022-06-06