自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

神器 Logging,你真的了解嗎?

開發(fā) 前端
logging 是 python 標(biāo)準(zhǔn)模塊,用于記錄和處理程序中的日志。功能很強大,官方文檔很詳細(xì),網(wǎng)上也有大量的說明和教程,但是對很多初次接觸的同學(xué)來說,存在一些障礙。

[[434022]]

本文轉(zhuǎn)載自微信公眾號「Python技術(shù)」,作者派森醬。轉(zhuǎn)載本文請聯(lián)系Python技術(shù)公眾號。

logging 是 python 標(biāo)準(zhǔn)模塊,用于記錄和處理程序中的日志。

功能很強大,官方文檔很詳細(xì),網(wǎng)上也有大量的說明和教程,但是對很多初次接觸的同學(xué)來說,存在一些障礙。

一是因為標(biāo)準(zhǔn)庫文檔太過繁瑣,需要較高的理論基礎(chǔ),著急用時,常常被文檔搞暈。

二是大部分說明材料要么是官方文檔的羅列,要么是簡單的應(yīng)用,對實際應(yīng)用幫助不大。

今天,我們從應(yīng)用上的一些問題開始,探討一下日志神器 logging 極其背后的原理,讓它能真正的幫助到我們。

該用 logging.debug 還是 logger.debug ?

debug 是日志模塊中的一個日志等級為 DEBUG 的日志生成方法,還有 info、warning、error、critial,這里用 debug 做為代表進(jìn)行說明。

我們常會看到,一會兒用 logging.debug 記錄日志,一會兒又用 logger.debug 記錄日志,到底該用什么?

先看代碼:

  1. import logging 
  2.  
  3. logging.debug('調(diào)試信息'
  4.  
  5. logger = logging.getLogger() 
  6.  
  7. logger.debug('調(diào)試信息'

首先 logging 是作為一個模塊被引入的。logging.debug 用的是 logging 模塊的模塊方法。

logger 是用 logging.getLogger() 生成的,是一個 日志對象,logger.debug 調(diào)用的是 logger 這個日志對象的方法。

上面的代碼中 logging.debug 和 logger.debug 的效果完全是一樣的。

這是因為,為了讓開發(fā)者方便使用,logging 模塊提供了一些列模塊方法,如 debug,在引入模塊后,就可以直接使用。這樣開發(fā)者就不必關(guān)心日志模塊的細(xì)節(jié),像用 print 一樣輸出日志。

如果需要對日志輸出進(jìn)行定制化,比如將日志輸出到文件中,過濾某些級別的日志,就需要創(chuàng)建或者得到一個實際的日志對象來處理,如上面代碼中通過 getLogger 方法得到的日志對象。

我們知道,程序設(shè)計里要避免重復(fù)的設(shè)計,如果模塊方法采用一套機制,日志對象上的方法采用另一套機制,就會出現(xiàn)重復(fù)造輪子的問題。

所以在使用模塊方法,logging 其實創(chuàng)建了一個日志對象 —— root logger。

也就是 logging.debug 這個調(diào)用,實質(zhì)上是調(diào)用 root logger 的日志方法。

相當(dāng)于默認(rèn)情況下 root logger 會作為日志處理對象。

如何獲得 root logger 對象呢?

通過不帶參數(shù)的 logging.getLogger() 方法獲得。

那么 logging.debug 和 rootLogger.debug 是一會事,可以理解(但不嚴(yán)謹(jǐn))為 logging.debug 是 rootlogger.debug 的快捷方式。

日志樹

稍加留意就會觀察到,程序是有層次結(jié)構(gòu)的,通過相互引用,調(diào)用形成一個樹狀結(jié)構(gòu)。

程序加載的地方是樹根,比如 python 中要運行的代碼文件,我們稱之為 main。從樹根開始長出其他枝葉。對于一個模塊來說,又會形成一個自己的樹。

如何用日志清楚地記錄層次結(jié)構(gòu)呢?

雖然直接打印出調(diào)用堆棧也可以看到調(diào)用結(jié)構(gòu),不過不太直觀,缺乏業(yè)務(wù)邏輯描述。

而用 print 來打印出層次結(jié)構(gòu),需要編寫大量的代碼才能反射出(通過運行狀態(tài)獲取代碼狀態(tài)的一種方式)調(diào)用環(huán)境。

logging 提供了完畢的解決方案。

前面提到的 root logger 就是整個日志樹的根,其他所有的 logger 都是從 root logger 伸展出來的枝葉。只要通過 getLogger(loggername) 方法獲得的 logger 對象,都是伸展自 root logger 的。

如何向下伸展呢?

很簡單,就像引用模塊的層次關(guān)系一樣,用 . 分隔層次就好了,例如:

  1. logger = logging.getLogger('mod1.mod2.mod3'
  2.  
  3. logger.debug("調(diào)試信息"

語句 logging.getLogger('mod1.mod2.mod3') 實際上創(chuàng)建了三個 logger,名稱分別是 mod1、mod1.mod2 和 mod1.mod2.mod3

mod1 為根,mod1.mod2 為子,mod1.mod2.mod3 為孫。

如果在 mod1 上設(shè)置了日志處理器(handler),那么其他兩個的日志對象都會用到這個處理器。

這樣不但記錄的日志更清晰而且,可以為同一個根的日志對象設(shè)置可以共享的日志處理方式。

這樣感覺也不方便,需要些那么多層次,如何才能更方便呢?在下面的 實踐參考 里會有說明。

logging.basicConfig 的功與過

說完了日志模塊的樹狀結(jié)構(gòu),來看看一個很常用的設(shè)置方法 basicConfig。

它可以方便的設(shè)置日志處理和記錄方式,如沒必要,不用為每個日志對象單獨設(shè)置。

根據(jù)第一節(jié)的分析,我們知道,直接使用模塊方法,用的其實是 root logger,那么就能明白 basicConfig 設(shè)置了 root logger 的日志處理方式。

這就意味著:

一旦設(shè)置了通過 logging.basicConfig 設(shè)置了日志處理方式,其他所有日志都很受到影響。

另外 basicConfig 是個一次性方法,即:

只有第一次設(shè)置有效,其后設(shè)置無效

本來是個一勞永逸的方法。

但用錯了地方,就會很麻煩。

看下例子:

  1. __all__ = ['Connection''ConnectionPool''logger'
  2.  
  3. warnings.filterwarnings('error', category=pymysql.err.Warning) 
  4. # use logging module for easy debug 
  5. logging.basicConfig(format='%(asctime)s %(levelname)8s: %(message)s', datefmt='%m-%d %H:%M:%S'
  6. logger = logging.getLogger(__name__) 
  7. logger.setLevel('WARNING'

這段代碼中,用 logging.basicConfig 對日志做了設(shè)置,意思是后面的日志都按照這樣的方式輸出。

但它是一個底層模塊 —— pymysqlpool[1]。

pymysqlpool 封裝了 pymysql[2] 模塊,提供了鏈接池特性,在多線程處理數(shù)據(jù)庫場景下很有用。

也就是說,pymysqlpool 只會被引用加載,不會作為 main 被加載,這就比較尷尬了,因為 main 中對日志的設(shè)置就沒有效果。

作為一個服務(wù)類模塊(相對于業(yè)務(wù)的底層模塊),不要通過 basicConfig 來設(shè)置日志模式,要么通過自己專屬的日志對象來設(shè)置,要么不去設(shè)置,統(tǒng)一交給 main 去設(shè)置,例如:

  1. logger = logging.getLogger(__name__) 
  2.  
  3. fmt = logging.Formatter("%(asctime)s %(levelname)8s: %(message)s", datefmt='%m-%d %H:%M:%S'
  4. hdl = logging.StreamHandler() 
  5. hdl.setFormatter(fmt) 
  6. logger.addHandler(hdl) 
  7.  
  8. logger.setLevel('WARNING'

如果為了測試,可以在測試的初始化方法中,使用 basicConfig 來設(shè)置,因為測試時,模塊往往是被作為程序入庫加載的。

實踐參考

了解了日志模塊的一下特性,和其中的原理之后,這里有幾條實踐參考。

  • 不要再子模塊中使用 logging.basicConfig 設(shè)置日志模式
  • 強烈建議在任何模塊中通過 logger = logging.getLogger(__name__) 來創(chuàng)建日志對象 因為 __name__ 代表的就是模板被加載的引用名稱。

例如 from a.b.c import b 模塊 c 中的 __name__ 值就為 a.b.c。

  • 而且這個引用名稱剛好符合 logger 定義的層次結(jié)構(gòu)。

通過命令行參數(shù)設(shè)置不同類型的日志,見代碼:

  1. import logging 
  2. import argparse 
  3. logger = logging.getLogger(__name__) 
  4.  
  5. def create_args_parse(): 
  6.     parser = argparse.ArgumentParser(description="參數(shù)列表"
  7.     parser.add_argument('-d''--debug'action='store_true', help='調(diào)試模式'
  8.     # 加入其他命令行參數(shù) 
  9.          
  10.     return parser 
  11.  
  12. def set_logger(debug): 
  13.     formatter = logging.Formatter('%(asctime)s - %(levelname)8s - %(name)s - %(filename)s:%(lineno)d - %(thread)d- %(funcName)s:\t%(message)s'
  14.     if debug: 
  15.         hd = logging.StreamHandler() 
  16.         logger.setLevel(logging.DEBUG) 
  17.         hd.setFormatter(formatter) 
  18.     else
  19.         hd = logging.FileHandler(f'{__name__}.log''a', encoding='utf-8'
  20.         logger.setLevel(logging.INFO) 
  21.         hd.setFormatter(formatter) 
  22.     logger.addHandler(hd) 
  23.  
  24. if __name__ == '__main__'
  25.    parser = create_args_parse() 
  26.    args = parser.parse_args() 
  27.    debug = args.debug 
  28.    set_logger(debug) 
  29.    ... 

代碼有點長,但不難懂。

  • create_args_parse 方法用于解析命令行參數(shù),其中定義了一個 debug 參數(shù),表示開啟調(diào)試模式
  • set_logger 方法接收一個是否為調(diào)試模式的參數(shù),根據(jù)是否為調(diào)試模式,設(shè)置不同的日志模式
  • main 中,首先調(diào)用 create_args_parse 獲得命令行參數(shù)對象,然后從中解析出參數(shù),提取 debug 模式,傳送給 set_logger 方法,設(shè)置日志模式
  • 這樣只需要在運行程序時,加上參數(shù) -d 就可以讓日志打印到終端上,不加,日志就會自動去 __main__.log 日志文件中去了。

總結(jié)

python 為我們提供了很多便利的功能,有些需要真的用到才能有所體會,所以在遇到問題時,需要多研究一下,找到其中的特點和內(nèi)在的原理或機制,這樣就能更好的應(yīng)用了。

在我理解了 logging 的原理之后,已經(jīng)在我的很多項目中發(fā)揮了巨大作用,而且再也不必糾結(jié)于怎么用,如何更合理等這些問題了。

期望這篇文章也能對你有所幫助,比心。

參考資料

[1]pymysqlpool: https://pypi.org/project/pymysql-pool/

[2]pymysql: https://pypi.org/project/PyMySQL/

 

責(zé)任編輯:武曉燕 來源: Python技術(shù)
相關(guān)推薦

2022-07-26 00:00:22

HTAP系統(tǒng)數(shù)據(jù)庫

2014-04-17 16:42:03

DevOps

2019-09-16 08:40:42

2014-11-28 10:31:07

Hybrid APP

2020-02-27 10:49:26

HTTPS網(wǎng)絡(luò)協(xié)議TCP

2023-03-16 10:49:55

2021-01-15 07:44:21

SQL注入攻擊黑客

2025-01-03 08:09:15

2024-02-02 08:50:20

Node.js元數(shù)據(jù)自動化

2017-10-18 22:01:12

2023-10-24 08:53:24

FutureTas并發(fā)編程

2012-05-31 09:56:54

云安全

2015-07-31 10:35:18

實時計算

2022-12-12 08:46:11

2019-11-06 09:52:01

JavaScript單線程非阻塞

2022-03-14 07:53:27

ELTETL大數(shù)據(jù)

2023-05-10 11:07:18

2016-01-13 10:34:57

物聯(lián)網(wǎng)物聯(lián)網(wǎng)技術(shù)

2018-01-06 10:38:51

Ping抓包 ICMP協(xié)議

2010-08-16 14:07:44

盒模型marginpadding
點贊
收藏

51CTO技術(shù)棧公眾號