python編程之logging模塊的使用

       編程中,很多時候我們需要保存一個程序的中間輸出,要了解一個程序的運行情況,記錄程序運行中在一些關鍵節點處的信息,以便我們後續進行分析和問題的排查。對於簡單的應用和相對不那麼複雜的程序,我們當然可以通過最常用的print來實現這些需求,無論是輸出到控制檯還是重定向到磁盤文件。但是一旦程序的功能性和複雜性提升了,我們一直使用print會顯得雜亂,而且print的意義有時候也不明顯,print一般只適合對於局部問題的排查,對於較多的信息記錄,print就顯得有些無力,因爲我們要讓記錄信息顯得規範而且意義明確,用print會比較繁瑣,而且也不高效。對此,python有一個標準庫logging,就是專門用以滿足這種需求的,其可以讓結果輸出更加的規範,意義更加明確,而且也可以對不同的信息進行管理,相當於是一個日誌管理工具。本文將基於官方文檔,總結一下loggin的用法,分爲基礎用法和高級用法兩部分。

基礎用法

       直接導入logging後,利用logging的函數來打印或者保存信息,基本的函數有:debug(),info(),warning(),error(),critical(),其等級依次遞增。所謂等級level的概念是用以表示一條消息的嚴重等級,比如debug是最低等級,info次之,表示其對應的消息對於程序來說不那麼嚴重,只是爲了記錄程序的運行情況,或者用以問題排查,而後面的warning則一般表示警告,表示程序出現了需要注意的結果,可能會對後續運行造成影響,以此類推。當然,這些函數名只是爲了區分不同的使用場景,以讓用戶更好的識別信息,如果硬是要對debug對應的場景用critical函數輸出或保存消息,也是可以的,消息也可以輸出和保存,只是意義變得混淆而已,不影響使用。

       logging中一共有5個level,對應不同的使用場景,具體的可以看下圖。

      如下所示,當運行如下語句時,會在控制檯輸出如下內容。

import logging

logging.info('info test')
logging.warning('warning test')
logging.critical('critical test')

# output:
# WARNING:root:warning test
# CRITICAL:root:critical test

       輸出的內容中,默認是以(level name):(logger name):(message)的格式,其中關於logger name,後面的高級用法部分會講到。從輸出的結果可以看到,logging.info語句的內容並沒有輸出,只輸出了warning和critical的內容,原因是logging默認只輸出level等於以及高於warging的信息,所以level爲info的便不會輸出;當然,我們可以通過設置level改變這個行爲,如下所示,同時logging輸出的地方默認是控制檯,當然也可以通過如下方式,即利用logging的basicConfig函數進行設置,將輸出的內容保存在一個文件中。

import logging

logging.basicConfig(filename='test.log',level=logging.DEBUG)
logging.info('info test')
logging.warning('warning test')
logging.critical('critical test')

# file content:
# INFO:root:info test
# WARNING:root:warning test
# CRITICAL:root:critical test

       如上所述,我們可以通過basigConfig這個函數進行多種設置,除了上述設置,還可以設置信息的格式,時間戳以及其格式,文件模式等,參數如下所示。

import logging
logging.basicConfig(filename='test.log',filemode='w',format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
logging.warning('is when this event was logged.')

# file content
# 12/12/2010 11:46:36 AM is when this event was logged.

       上述就是關於logging的基礎用法,至此我們已經可以對一個程序在logging的模塊層面進行日誌記錄,並以特定的格式保存。下面我們再介紹一些高級一點的用法,以更多更好的滿足我們的工作需求。

高級用法

       高級用法將不再僅僅利用模塊層面的函數,而是以幾個基本的對象爲基礎:logger對象、handler對象以及formatter對象,有時候還會用大filter對象。在本文中,將主要簡單介紹一下logger、handler和formatter對象。

       logger對象通過logging.getLogger(name)構造,其是直接和腳本進行交流,併發送源log信息的對象,其依然是通過debug()、info()這些函數來生成log信息;handler對象的作用是將logger產生的log信息進行一個分發,一個logger可以添加多個handler對象,從而將log信息分發到不同的地方;比如,可以將所有log信息通過FileHandler保存在一個本地文件中,同時將WARNING以上的信息通過StreamHandler輸出到控制檯。log信息的內容和格式是很重要的,因此我們可以通過formatter對象給log信息設置格式。下面的代碼展示了這些對象的簡單使用方式。

import logging

logger = logging.getLogger('test')
logger.setLevel(logging.DEBUG)

fh = logging.FileHandler('test1.log',mode='a',encoding='utf8')
fh.setLevel(logging.DEBUG)

ch = logging.StreamHandler()
ch.setLevel(logging.ERROR)

formatter = logging.Formatter('%(asctime)s - %(name)-8s - %(levelname)-8s - %(message)s',datefmt='%Y-%m-%d %H:%M:%S')
fh.setFormatter(formatter)
ch.setFormatter(formatter)

logger.addHandler(fh)
logger.addHandler(ch)

格式設置說明

      日誌信息的格式設置是很重要的,首先對於message格式的設置是完全和字符串的格式化表達一樣的,對格式化表達式內容的填充可以通過info,debug等這些函數的位置參數傳入,比如info(msg_fmt,s1,s2,extra=d),其中msg_fmt是字符串格式化表達式,而s1,s2表示對其的內容的填充,最後還有一個extra參數,其是一個字典對象,其填充的不是msg_fmt,而是通過formatter對象設置前置格式時需要填充的內容。

       對於fromatter對象,我們設置格式時,可以看到其中有一些類似asctime,name,levelname這樣的字符串,這些字符串實際上是logRecord的屬性,logRecord對象是一條日誌信息的封裝,每次調用info這些接口時會自動生成對應的logRecord對象;參考上述示例代碼中對於格式的設置,類似'%(cs)-8s'%{'cs':'haha'} -->'haha    '  or  '%(cs)8s'%{'cs':'haha'}-->'    haha',上述的示例中其中用的括號中的字符串實際上都是logRecord的屬性,我們還可以設置其他的屬性,比如行數,以方便我們定位日誌在腳本中所在的位置,定義的方式實際上就是字符串格式化表達式,其中括號表示後續填充內容的字典的key。對於logRecord對象的屬性,可看下圖,我們可以在實際應用中自己利用這些屬性設置格式,以下屬性基本可以滿足我們大部分的需求,當然如果我們有自定義的字段,可以在調用Info這些接口時通過extra參數進行傳入。

       最後還需要說明的一點是,logging是線程安全的,即我們可以通過多個線程寫入同一個日誌文件,其會自動加鎖,但是並不是進程安全的,要實現進程安全,可以參考官方文檔,當然,更多的高級功能,也可以參看官方的logging cookbook

 

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