Python logging模塊用法記錄

原文:https://www.cnblogs.com/deeper/p/7404190.html

啄木鳥社區裏的Pythonic八榮八恥有一條:

以打印日誌爲榮 , 以單步跟蹤爲恥;

很多程序都有記錄日誌的需求,並且日誌中包含的信息既有正常的程序訪問日誌,還可能有錯誤、警告等信息輸出,python的logging模塊提供了標準的日誌接口,你可以通過它存儲各種格式的日誌,主要用於輸出運行日誌,可以設置輸出日誌的等級、日誌保存路徑、日誌文件回滾等;

爲什麼不用print打印輸出?

這種方式對於簡單腳本型程序有用,但是如果是複雜的系統,最好不要用。首先,這些print是沒用的輸出,大量使用很有可能會被遺忘在代碼裏。再者,print 輸出的所有信息都到了標準輸出中,這將嚴重影響到你從標準輸出中查看其它輸出數據。

使用logging的優勢:

a)你可以控制消息的級別,過濾掉那些並不重要的消息。

b)你可決定輸出到什麼地方,以及怎麼輸出。有許多的重要性別級可供選擇,debug、info、warning、error 以及 critical。通過賦予 logger 或者 handler 不同的級別,你就可以只輸出錯誤消息到特定的記錄文件中,或者在調試時只記錄調試信息。

下面讓我們正式進入logging的世界:

1. logging日誌框架

主要包括四部分:

  • Loggers: 可供程序直接調用的接口,app通過調用提供的api來記錄日誌
  • Handlers: 決定將日誌記錄分配至正確的目的地
  • Filters:對日誌信息進行過濾, 提供更細粒度的日誌是否輸出的判斷
  • Formatters: 制定最終記錄打印的格式佈局

1)loggers

loggers 就是程序可以直接調用的一個日誌接口,可以直接向logger寫入日誌信息。logger並不是直接實例化使用的,而是通過logging.getLogger(name)來獲取對象,事實上logger對象是單例模式,logging是多線程安全的,也就是無論程序中哪裏需要打日誌獲取到的logger對象都是同一個。但是不幸的是logger並不支持多進程,這個在後面的章節再解釋,並給出一些解決方案。

【注意】loggers對象是有父子關係的,當沒有父logger對象時它的父對象是root,當擁有父對象時父子關係會被修正。舉個例子,logging.getLogger("abc.xyz") 會創建兩個logger對象,一個是abc父對象,一個是xyz子對象,同時abc沒有父對象,所以它的父對象是root。但是實際上abc是一個佔位對象(虛的日誌對象),可以沒有handler來處理日誌。但是root不是佔位對象,如果某一個日誌對象打日誌時,它的父對象會同時收到日誌,所以有些使用者發現創建了一個logger對象時會打兩遍日誌,就是因爲他創建的logger打了一遍日誌,同時root對象也打了一遍日誌。

2)Handlers

Handlers 將logger發過來的信息進行準確地分配,送往正確的地方。舉個栗子,送往控制檯或者文件或者both或者其他地方(進程管道之類的)。它決定了每個日誌的行爲,是之後需要配置的重點區域。

每個Handler同樣有一個日誌級別,一個logger可以擁有多個handler也就是說logger可以根據不同的日誌級別將日誌傳遞給不同的handler。當然也可以相同的級別傳遞給多個handlers這就根據需求來靈活的設置了。

3)Filters

Filters 提供了更細粒度的判斷,來決定日誌是否需要打印。原則上handler獲得一個日誌就必定會根據級別被統一處理,但是如果handler擁有一個Filter可以對日誌進行額外的處理和判斷。例如Filter能夠對來自特定源的日誌進行攔截or修改甚至修改其日誌級別(修改後再進行級別判斷)。

logger和handler都可以安裝filter甚至可以安裝多個filter串聯起來。

4) Formatters

Formatters 指定了最終某條記錄打印的格式佈局。Formatter會將傳遞來的信息拼接成一條具體的字符串,默認情況下Format只會將信息%(message)s直接打印出來。Format中有一些自帶的LogRecord屬性可以使用,如下表格:

一個Handler只能擁有一個Formatter 因此如果要實現多種格式的輸出只能用多個Handler來實現。

上圖只是一部分,更詳細的在docs.python.org裏找logging模塊:

2. 日誌級別

在記錄日誌時, 日誌消息都會關聯一個級別(“級別”本質上是一個非負整數)。系統默認提供了6個級別,它們分別是:

可以給日誌對象(Logger Instance)設置日誌級別,低於該級別的日誌消息將會被忽略,也可以給Hanlder設置日誌級別,對於低於該級別的日誌消息, Handler也會忽略。

3. 常用函數

如果想要快速碼代碼,可以直接看第4節,基本使用

1)logging.basicConfig([**kwargs]):

爲日誌模塊配置基本信息。kwargs 支持如下幾個關鍵字參數:
filename :日誌文件的保存路徑。如果配置了些參數,將自動創建一個FileHandler作爲Handler;
filemode :日誌文件的打開模式。 默認值爲’a’,表示日誌消息以追加的形式添加到日誌文件中。如果設爲’w’, 那麼每次程序啓動的時候都會創建一個新的日誌文件;
format :設置日誌輸出格式;
datefmt :定義日期格式;
level :設置日誌的級別.對低於該級別的日誌消息將被忽略;
stream :設置特定的流用於初始化StreamHandler;

2)logging.getLogger([name])

創建Logger對象。日誌記錄的工作主要由Logger對象來完成。在調用getLogger時要提供Logger的名稱(注:多次使用相同名稱來調用getLogger,返回的是同一個對象的引用。),Logger實例之間有層次關係,這些關係通過Logger名稱來體現,如:

p = logging.getLogger(“root”)

c1 = logging.getLogger(“root.c1”)

c2 = logging.getLogger(“root.c2”)

例子中,p是父logger, c1,c2分別是p的子logger。c1, c2將繼承p的設置。如果省略了name參數, getLogger將返回日誌對象層次關係中的根Logger。

3)logging.getLevelName(lvl)

獲取日誌級別對應的名稱。例如:

複製代碼

1 import logging  
2   
3 #下面的結果都爲DEBUG  
4 print logging.getLevelName(10)  
5 print logging.getLevelName(logging.DEBUG)  
6 #下面的結果都爲ERROR  
7 print logging.getLevelName(40)  
8 print logging.getLevelName(logging.ERROR)  

複製代碼

4)logging.shutdown()

當不再使用日誌系統的時候,調用該方法,它會將日誌flush到對應的目標域上。一般在系統退出的時候調用。

Logger對象通過調用logging.getLogger(name)來創建,它有如下常用的方法和屬性:

2 - a)Logger.setLevel(lvl):

設置日誌的級別。對於低於該級別的日誌消息將被忽略。下面一個例子演示setLevel方法:

複製代碼

 1 logger = logging.getLogger('root.test')  
 2 logger.setLevel(logging.INFO)  
 3 console_handler = logging.StreamHandler()  
 4 console_handler.setLevel(logging.WARNING)  
 5 logger.addHandler(console_handler)  
 6   
 7 logger.info('info') #不會記錄  
 8 logger.debug('debug') #不會記錄  
 9 logger.warning('warning') #記錄warning  
10 logger.error('error')   #記錄error  

複製代碼

2 - b)打印消息

Logger.debug(msg [ ,*args [, **kwargs]])

記錄DEBUG級別的日誌信息。參數msg是信息的格式,args與kwargs分別是格式參數。

Logger.info(msg[ , *args[ , **kwargs] ] )

Logger.warnning(msg[ , *args[ , **kwargs] ] )

Logger.error(msg[ , *args[ , **kwargs] ] )

Logger.critical(msg[ , *args[ , **kwargs] ] )

記錄相應級別的日誌信息。參數的含義與Logger.debug一樣。

2 - c)增加移除處理器

Logger.addHandler(hdlr)

Logger.removeHandler(hdlr)

添加/移除日誌消息處理器。在講述Handler時具體介紹。

2 - d)Logger.log(lvl, msg[ , *args[ , **kwargs] ] )

記錄日誌,參數lvl用戶設置日誌信息的級別。參數msg, *args, **kwargs的含義與Logger.debug一樣。

2 - e)Logger.exception(msg[, *args])

以ERROR級別記錄日誌消息,異常跟蹤信息將被自動添加到日誌消息裏。Logger.exception通過用在異常處理塊中,如:

複製代碼

1 #異常信息也會添加到日誌消息裏
2 try:
3     raise IOError, 'this is a winter testing IOError'
4 except IOError:
5     logger.exception('record this error')
6     logger.warning(''.center(50,'-'))

複製代碼

2 - f)

Logger.addFilter(filt)

Logger.removeFilter(filt)

添加/移除日誌消息過濾器。在講述Filter時具體介紹。

2 - g)Logger.makeRecord(name, lvl, fn, lno, msg, args, exc_info[, func, extra])

創建LogRecord對象。日誌消息被實例爲一個LogRecord對象,並在日誌類內處理。

4.基本使用

1) 簡單的將日誌打印到屏幕

複製代碼

1 import logging  
2   
3 logging.debug('this is debug message')  
4 logging.info('this is info message')  
5 logging.warning('this is warning message')  
6   
7 #打印結果:WARNING:root:this is warning message  

複製代碼

默認情況下,logging將日誌打印到屏幕,日誌級別爲WARNING;
日誌級別大小關係爲:CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET,當然也可以自己定義日誌級別。

2)通過logging.basicConfig函數對日誌的輸出格式及方式做相關配置

複製代碼

 1 import logging  
 2   
 3 logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(message)s')  
 4   
 5 logging.debug('this is debug message')  
 6 logging.info('this is info message')  
 7 logging.warning('this is warning message')  
 8   
 9 ''''' 
10 結果: 
11 2017-08-23 14:22:25,713 - root - this is debug message 
12 2017-08-23 14:22:25,713 - root - this is info message 
13 2017-08-23 14:22:25,714 - root - this is warning message 
14 '''  

複製代碼

logging.basicConfig函數各參數:
filename: 指定日誌文件名
filemode: 和file函數意義相同,指定日誌文件的打開模式,'w'或'a'
format: 指定輸出的格式和內容,format可以輸出很多有用信息,如上例所示:
 %(levelno)s: 打印日誌級別的數值
 %(levelname)s: 打印日誌級別名稱
 %(pathname)s: 打印當前執行程序的路徑,其實就是sys.argv[0]
 %(filename)s: 打印當前執行程序名
 %(funcName)s: 打印日誌的當前函數
 %(lineno)d: 打印日誌的當前行號
 %(asctime)s: 打印日誌的時間
 %(thread)d: 打印線程ID
 %(threadName)s: 打印線程名稱
 %(process)d: 打印進程ID
 %(message)s: 打印日誌信息
datefmt: 指定時間格式,同time.strftime()
level: 設置日誌級別,默認爲logging.WARNING
stream: 指定將日誌的輸出流,可以指定輸出到sys.stderr,sys.stdout或者文件,默認輸出到sys.stderr,當stream和filename同時指定時,stream被忽略

3)將日誌同時輸出到文件和屏幕

按 Ctrl+C 複製代碼

 

按 Ctrl+C 複製代碼

可以在log.txt文件和控制檯中看到:

可以發現,logging有一個日誌處理的主對象,其他處理方式都是通過addHandler添加進去,logging中包含的handler主要有如下幾種:

4)傳給syslogserver,郵箱

複製代碼

1 from logging import handlers  
2   
3 #日誌傳送到syslog server  
4 syslog_handler = handlers.SysLogHandler(address=('192.168.168.1', 514))  
5   
6 #日誌傳送給郵箱  
7 mail_handler = handlers.SMTPHandler('192.168.168.1', '[email protected]', '[email protected]', 'subject')
8 #郵件給多人
9 mail_handler = handlers.SMTPHandler('192.168.168.1', '[email protected]', ('[email protected]', '[email protected]'), 'subject')

複製代碼

5)日誌回滾

如果你用 FileHandler 寫日誌,文件的大小會隨着時間推移而不斷增大。最終有一天它會佔滿你所有的磁盤空間。爲了避免這種情況出現,你可以在你的生成環境中使用 RotatingFileHandler 替代 FileHandler。

複製代碼

 1 import logging  
 2 from logging.handlers import RotatingFileHandler  
 3 logger = logging.getLogger(__name__)  
 4 logger.setLevel(level = logging.INFO)  
 5 #定義一個RotatingFileHandler,最多備份3個日誌文件,每個日誌文件最大1K  
 6 rHandler = RotatingFileHandler("log.txt",maxBytes = 1*1024,backupCount = 3)  
 7 rHandler.setLevel(logging.INFO)  
 8 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')  
 9 rHandler.setFormatter(formatter)  
10    
11 console = logging.StreamHandler()  
12 console.setLevel(logging.INFO)  
13 console.setFormatter(formatter)  
14    
15 logger.addHandler(rHandler)  
16 logger.addHandler(console)  
17    
18 logger.info("Start print log")  
19 logger.debug("Do something")  
20 logger.warning("Something maybe fail.")  
21 logger.info("Finish")  

複製代碼

5. 多模塊使用logging配置

1)通過繼承關係實現

複製代碼

 1 import logging
 2 
 3 logger = logging.getLogger('mainModule')
 4 logger.setLevel(level=logging.INFO)
 5 
 6 formatter = logging.Formatter(
 7     '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
 8 
 9 handler = logging.FileHandler('log.txt')
10 handler.setLevel(logging.INFO)
11 handler.setFormatter(formatter)
12 
13 console = logging.StreamHandler()
14 console.setLevel(logging.INFO)
15 console.setFormatter(formatter)
16 
17 logger.addHandler(handler)
18 logger.addHandler(console)
19 
20 # 在其他模塊導入該日誌接口module_logger即可
21 module_logger = logging.getLogger('mainModule.sub')
22 module_logger.info('this is another module using logging')

複製代碼

說明:

首先定義了logger'mainModule',並對它進行了配置,就可以在解釋器進程裏面的其他地方通過getLogger('mainModule')得到的對象都是一樣的,不需要重新配置,可以直接使用。定義的該logger的子logger,都可以共享父logger的定義和配置,所謂的父子logger是通過命名來識別,任意以'mainModule'開頭的logger都是它的子logger,例如'mainModule.sub'。

實際開發一個application,首先可以通過logging配置文件編寫好這個application所對應的配置,可以生成一個根logger,如'PythonAPP',然後在主函數中通過fileConfig加載logging配置,接着在application的其他地方、不同的模塊中,可以使用根logger的子logger,如'PythonAPP.Core','PythonAPP.Web'來進行log,而不需要反覆的定義和配置各個模塊的logger。

2)通過logging.config模塊配置日誌

View Code

example01

View Code

example02

View Code

python2.7以後,可以從字典中加載logging配置,也就意味着可以通過JSON或者YAML文件加載日誌的配置

3)通過JSON加載日誌配置

View Code

通過JSON加載配置文件,然後通過logging.dictConfig配置logging,

View Code

使用JSON的一個優點就是json是一個標準庫,不需要額外安裝。但是,有人更喜歡YAML。無論讀起來還是寫起來都比較容易

4)通過YAML文件配置

通過YAML文件進行配置,比JSON看起來更加簡介明瞭,

View Code

通過YAML加載配置文件,然後通過logging.dictConfig配置logging

View Code

 接下來,你就可以在運行程序的時候調用setup_logging來啓動日誌記錄了。它默認會讀取logging.json或logging.yaml文件。你也可以設置環境變量LOG_CFG從指定的路徑加載日誌配置,例如:

LOG_CFG = my_logging.json python my_server.py

如果你喜歡YAML:

LOG_CFG = my_logging.yaml python my_server.py

 

注意:配置文件中“disable_existing_loggers” 參數設置爲 False;如果不設置爲False,創建了 logger,然後你又在加載日誌配置文件之前就導入了模塊。logging.fileConfig 與 logging.dictConfig 默認情況下會使得已經存在的 logger 失效。那麼,這些配置信息就不會應用到你的 Logger 上。“disable_existing_loggers” = False解決了這個問題

 

 6. 捕捉異常並使用traceback記錄它

出問題時記錄下來是個好習慣,python中的traceback模塊用於記錄異常信息,我們可以在logger中記錄下traceback

比如下面的例子:

View Code

結果爲:

ERROR:__main__:Failed to open file  
Traceback (most recent call last):  
  File "example.py", line 6, in <module>  
    open('/path/to/does/not/exist', 'rb')  
IOError: [Errno 2] No such file or directory: '/path/to/does/not/exist'  

你也可以調用 logger.exception(msg, _args),它等價於 logger.error(msg, exc_info=True, _args)。

 logger.error('Failed to open file', exc_info=True)  

替換爲:

logger.exception('Failed to open file')  

7. logger間的繼承關係

1) logger間存在繼承關係

logger 通過名字來決定繼承關係,如果一個logger的名字是"mydest",另一個logger的名字是"mydest.dest1" (getLogger("mydest.dest1")),那麼就稱後者是前者的子logger,會繼承前者的配置。上面的代碼沒有指定logger,直接調用logging.debug等方法時,會使用所有logger的祖先類RootLogger

從上面的代碼運行結果可以猜測出,該RootLogger設置的日誌級別是logging.WARN,輸出目的地是標準流。從源碼可以更清楚的看出來:

root = RootLogger(WARNING)  #設置root用戶的日誌級別爲WARNING  

至於rootLogger的輸出目的地的配置,我們跟蹤logging.debug的源代碼來看一下:

View Code

大約可以看到,如果rootLogger沒有配置handler,就會不帶參數運行basicConfig函數,我們看一下basicConfig的源代碼:

View Code

因爲參數爲空,所以我們就看出了,該rootLoger使用了不帶參數的StreamHandler,也可以看到諸如format之類的默認配置。之後我們 跟蹤StreamHandler(因爲我們想看到日誌輸出目的地的配置,而handler就是控制日誌流向的,所以我們要跟蹤它)的源代碼:

View Code

不帶參數的StreamHandler將會把日誌流定位到sys.stderr流,標準錯誤流同樣會輸出到控制檯。

2) basicConfig函數用來配置RootLogger

basicConfig函數僅用來配置RootLogger,rootLogger是所有Logger的祖先Logger,所以其他一切Logger會繼承該Logger的配置。

3) 通過示例詳細討論Logger配置的繼承關係

首先準備下繼承條件:log2繼承自log1,logger的名稱可以隨意,要注意‘.’表示的繼承關係。

View Code

a)level的繼承

原則:子logger寫日誌時,優先使用本身設置了的level;如果沒有設置,則逐層向上級父logger查詢,直到查詢到爲止。最極端的情況是,使用rootLogger的默認日誌級別logging.WARNING。

從源代碼中看更爲清晰, 感謝python的所見即所得:

View Code

b)handler的繼承

原則:先將日誌對象傳遞給子logger的所有handler處理,處理完畢後,如果該子logger的propagate屬性沒有設置爲0,則將日誌對象向上傳遞給第一個父Logger,該父logger的所有handler處理完畢後,如果它的propagate也沒有設置爲0,則繼續向上層傳遞,以此類推。最終的狀態,要麼遇到一個Logger,它的propagate屬性設置爲了0;要麼一直傳遞直到rootLogger處理完畢。

在上面實例代碼的基礎上,我們再添加一句代碼,即:

View Code

輸出結果爲:

display
[]

說好的繼承,但是子logger竟然沒有綁定父類的handler,what's wrong?

看到下面調用handler的源代碼,就真相大白了。可以理解成,這不是真正的(類)繼承,只是"行爲上的繼承":

View Code

額,最簡單的樣例牽引出來這麼多後臺的邏輯,不過我們懂一下也是有好處的。

8. logging遇到多進程

在handler中有一個class配置,可能有些讀者並不是很懂。其實這個是logging裏面原先就寫好的一些handler類,你可以在這裏直接調用。class指向的類相當於具體處理的Handler的執行者。在logging的文檔中可以知道這裏所有的Handler類都是線程安全的(但是GIL的存在,多線程基本可以無視),大家可以放心使用。所以一般情況下python要實現並行操作或者並行計算的時候都是使用多進程。但是logging並不是進程安全的,如果是用多進程來輸出日誌,則只有一個進程會切換,其他進程會在原來的文件中繼續打,還有可能某些進程切換的時候早就有別的進程在新的日誌文件裏打入東西了,那麼他會無情刪掉之,再建立新的日誌文件。會很亂很亂,完全沒法開心的玩耍。

現在我們就來解決這個問題,以日誌回滾使用的TimedRotatingFileHandler 這個類爲例:

1)線程不安全的原因

在解決之前,我們先看看爲什麼會導致這樣的原因。
先將TimedRotatingFileHandler 的源代碼貼上來,這部分是切換時所作的操作:

View Code

我們觀察 if os.path.exists(dfn) 這一行開始,這裏的邏輯是如果 dfn 這個文件存在,則要先刪除掉它,然後將 baseFilename 這個文件重命名爲 dfn 文件。然後再重新打開 baseFilename這個文件開始寫入東西。那麼這裏的邏輯就很清楚了

a)假設當前日誌文件名爲 current.log 切分後的文件名爲 current.log.2016-06-01

b)判斷 current.log.2016-06-01 是否存在,如果存在就刪除

c)將當前的日誌文件名 改名爲current.log.2016-06-01

d)重新打開新文件(我觀察到源代碼中默認是”a” 模式打開,之前據說是”w”)

於是在多進程的情況下,一個進程切換了,其他進程的句柄還在 current.log.2016-06-01 還會繼續往裏面寫東西。又或者一個進程執行切換了,會把之前別的進程重命名的 current.log.2016-06-01 文件直接刪除。又或者還有一個情況,當一個進程在寫東西,另一個進程已經在切換了,會造成不可預估的情況發生。還有一種情況兩個進程同時在切文件,第一個進程正在執行第3步,第二進程剛執行完第2步,然後第一個進程 完成了重命名但還沒有新建一個新的 current.log 第二個進程開始重命名,此時第二個進程將會因爲找不到 current 發生錯誤。如果第一個進程已經成功創建了 current.log 第二個進程會將這個空文件另存爲 current.log.2016-06-01。那麼不僅刪除了日誌文件,而且,進程一認爲已經完成過切分了不會再切,而事實上他的句柄指向的是current.log.2016-06-01。
好了這裏看上去很複雜,實際上就是因爲對於文件操作時,沒有對多進程進行一些約束,而導致的問題。
那麼如何優雅地解決這個問題呢。我提出了兩種方案,當然我會在下面提出更多可行的方案供大家嘗試。

2)解決方案1

先前我們發現 TimedRotatingFileHandler 中邏輯的缺陷。我們只需要稍微修改一下邏輯即可:

a)判斷切分後的文件 current.log.2016-06-01 是否存在,如果不存在則進行重命名。(如果存在說明有其他進程切過了,我不用切了,換一下句柄即可)

b)以”a”模式 打開 current.log
發現修改後就這麼簡單~
talking is cheap show me the code:

View Code

不要以爲代碼那麼長,其實修改部分就是 “##” 註釋的地方而已,其他都是照抄源代碼。這個類繼承了 TimedRotatingFileHandler 重寫了這個切分的過程。這個解決方案十分優雅,改換的地方非常少,也十分有效。但有網友提出,這裏有一處地方依然不完美,就是rename的那一步,如果就是這麼巧,同時兩個或者多個進程進入了 if 語句,先後開始 rename 那麼依然會發生刪除掉日誌的情況。確實這種情況確實會發生,由於切分文件一天才一次,正好切分的時候同時有兩個Handler在操作,又正好同時走到這裏,也是蠻巧的,但是爲了完美,可以加上一個文件鎖,if 之後加鎖,得到鎖之後再判斷一次,再進行rename這種方式就完美了。代碼就不貼了,涉及到鎖代碼,影響美觀。

3)解決方案2

我認爲最簡單有效的解決方案。重寫FileHandler類(這個類是所有寫入文件的Handler都需要繼承的TimedRotatingFileHandler 就是繼承的這個類;我們增加一些簡單的判斷和操作就可以。
我們的邏輯是這樣的:

a)判斷當前時間戳是否與指向的文件名是同一個時間

b)如果不是,則切換 指向的文件即可
結束,是不是很簡單的邏輯。
talking is cheap show me the code:

View Code

check_baseFilename 就是執行邏輯1判斷;build_baseFilename 就是執行邏輯2換句柄。就這麼簡單完成了。
這種方案與之前不同的是,當前文件就是 current.log.2016-06-01 ,到了明天當前文件就是current.log.2016-06-02 沒有重命名的情況,也沒有刪除的情況。十分簡潔優雅。也能解決多進程的logging問題。

4)解決方案3---進程安全的ConcurrentLogHandler

如果多進程多線程使用,推薦 ConcurrentLogHandler,需要安裝

安裝: 

pip install ConcurrentLogHandler

a)使用案例一

View Code

ConcurrentRotatingFileHandler參數說明: 
filename: 日誌文件地址,相對地址或絕對地址均可

mode: 默認爲"a"

maxBytes:文件長度,超過最大長度自動分片,最初日誌都會寫入filename裏面,到達設置的最大長度之後進行分片,分片後文件名爲filename.1 filename.2,以此類推

backupCount:最大日誌文件保留數量,默認爲0即不會刪除日誌文件

encoding:日誌文件編碼格式,默認爲gbk

b)使用案例二

建一個目錄,下面的文件都放到這個目錄中:

logging-config.yaml

View Code

 testlogging.py

View Code

9. 小知識點

1)使用__name__作爲logger的名稱

雖然不是非得將 logger 的名稱設置爲 __name__ ,但是這樣做會給我們帶來諸多益處。在 python 中,變量 __name__ 的名稱就是當前模塊的名稱。比如,在模塊 “foo.bar.my_module” 中調用 logger.getLogger(__name__) 等價於調用logger.getLogger(“foo.bar.my_module”) 。當你需要配置 logger 時,你可以配置到 “foo” 中,這樣包 foo 中的所有模塊都會使用相同的配置。當你在讀日誌文件的時候,你就能夠明白消息到底來自於哪一個模塊。

 2)全局共享logging配置

其實很簡單,只需要執行:

logging.basicConfig(filename=‘', level=logging.DEBUG, filemode='w', format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')

然後就可以在任何地方打印日誌即可

logging.leve('')

暫時就到這裏,以後繼續擴展

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