1 引言
最近在開發一個應用軟件,爲方便調試和後期維護,在代碼中添加了日誌,用的是Python內置的logging模塊,看了許多博主的博文,很有所得。不得不說,有許多博主大牛總結得確實很好。彷佛我再寫關於logging的博文有些多餘,但不寫總結又總以爲沒掌握。那就寫寫吧,也方便往後回顧。
開始總結以前,先感謝幾位博主,他們的博客寫得非常詳盡:
說說爲何須要添加日誌?
就像上面說的,爲了調試和後期維護方便。也許在開發中沒有太大致會,可是若是將軟件部署到了生產環境中,一旦出現bug,沒有日誌,就很難對當時的狀況進行追蹤,有了日誌,就能夠根據日誌儘量的對當時的數據環境進行還原,方便debug。甚至,只要日誌設計得足夠合理,還能夠用於後續業務數據分析等。
2 日誌等級
爲何須要對日誌進行劃分等級呢?
當咱們出於開發時debug的目的使用日誌時,咱們天然是想盡量詳盡得記錄日誌,可是若是部署到生產環境中,這樣作就可能由於大量的IO佔用服務器資源,因此在生產環境中就只須要記錄異常信息、錯誤狀況等就行了。
因此,給日誌設置等級,能夠方便得、因地制宜控制日誌輸出。
這裏只介紹Python的logging模塊的日誌等級(固然,其餘日誌系統的日誌等級劃分事實上也基本相同)。logging的日誌等級包括5個:
日誌等級(level)
|
描述
|
DEBUG
|
最詳細的日誌信息,典型應用場景是 問題診斷
|
INFO
|
信息詳細程度僅次於DEBUG,一般只記錄關鍵節點信息,用於確認一切都是按照咱們預期的那樣進行工做
|
WARNING
|
當某些不指望的事情發生時記錄的信息(如,磁盤可用空間較低),可是此時應用程序仍是正常運行的
|
ERROR
|
因爲一個更嚴重的問題致使某些功能不能正常運行時記錄的信息
|
CRITICAL
|
當發生嚴重錯誤,致使應用程序不能繼續運行時記錄的信息
|
日誌等級從上到下依次提升,當在程序中設定某個日誌等級以後,比設定的日誌等級低的日誌記錄將會被忽略,即logging就只會輸出大於和等於設定的等級的日誌。咱們將在下文中經過代碼示例證實這一點。
3 記錄日誌
logging模塊提供兩種方法記錄日誌:
(1)經過logging模塊提供的模塊級函數記錄日誌;
(2)經過logging模塊提供的4大組件記錄日誌。
3.1 記錄日誌之logging模塊級函數
在logging模塊中,分別給出一個模塊級別函數與上面說到的日誌級別相對應,用於輸出對應級別日誌記錄:
函數
|
說明
|
logging.debug(msg, *args, **kwargs)
|
建立一條嚴重級別爲DEBUG的日誌記錄
|
logging.info(msg, *args, **kwargs)
|
建立一條嚴重級別爲INFO的日誌記錄
|
logging.warning(msg, *args, **kwargs)
|
建立一條嚴重級別爲WARNING的日誌記錄
|
logging.error(msg, *args, **kwargs)
|
建立一條嚴重級別爲ERROR的日誌記錄
|
logging.critical(msg, *args, **kwargs)
|
建立一條嚴重級別爲CRITICAL的日誌記錄
|
也有一個函數彙總了上面5個函數的功能:
函數
|
說明
|
logging.log(level, *args, **kwargs)
|
建立一條嚴重級別爲level的日誌記錄
|
如今能夠來嘗試使用一下上面的函數了:
import logging
logging.debug('debug')
logging.info('info')
logging.warn('warn')
logging.error('error')
logging.critical('critical')
logging.warn('Today is %s',datetime.date.today())
運行結果以下:
WARNING:root:warn
ERROR:root:error
CRITICAL:root:critical
WARNING:root:Today is 2019-03-28
上面的函數都有
*args, **kwargs這兩個參數,因此這些函數能夠接受任意位置參數和關鍵字參數,這些參數填充到第一個參數msg,最後一條日誌輸出中添加了當前日期就是利用了這個功能。
那爲何會只輸出後面3條日誌記錄呢?上面說到過,logging就只會輸出大於和等於設定的等級的日誌記錄,而logging的默認日誌等級是WARNING,因此日誌等級爲DEBUG和INFO的兩條記錄都沒有被輸出。
若是想要輸入日誌等級爲DEBUG和INFO的日誌記錄,就要對logging進行配置。logging也提供了一個模塊級別的專用於配置logging的函數:
函數
|
說明
|
logging.basicConfig(**kwargs)
|
對root logger進行一次性配置
|
嘗試使用一下這個配置函數:
import logging
logging.basicConfig(level=logging.DEBUG) # 設置日誌等級
logging.debug('debug')
logging.info('info')
logging.warn('warn')
logging.error('error')
logging.critical('critical')
運行結果以下:
DEBUG:root:debug
INFO:root:info
WARNING:root:warn
ERROR:root:error
CRITICAL:root:critical
看,日誌等級爲DEBUG和INFO的兩條記錄也都獲得了輸出。
上面表格對
logging.basicConfig函數的說明中指出,logging.basicConfig函數時一次性配置,什麼意思呢?意思就是說,logging.basicConfig函數只在第一次運行(第一次對logging進行配置)時起做用,後面在此設置其餘參數是不會生效的。經過代碼證實一下:
import logging
logging.basicConfig(level=logging.DEBUG) # 設置日誌等級
logging.basicConfig(level=logging.INFO) # 從新設置日誌等級
logging.debug('debug')
logging.info('info')
logging.warn('warn')
logging.error('error')
logging.critical('critical')
運行結果:
DEBUG:root:debug
INFO:root:info
WARNING:root:warn
ERROR:root:error
CRITICAL:root:critical
看到沒,DEBUG級別日誌記錄仍是輸出了,證實從新運行
logging.basicConfig函數設置日誌級別沒有生效。
另外須要注意的是,
必定要在使用logging記錄日誌以前使用logging.basicConfig進行配置,不然,不會有任何輸出。
咱們再觀察一下上面的程序輸出,能夠發現,每一條輸出的結果裏,不只僅只有咱們輸出的字符串參數,還有其它的一些信息,例如日誌等級,日誌器名稱(默認是root),分隔符(這裏是冒號)等,這些都是logging默認給我配置好的,固然,咱們也能夠經過
logging.basicConfig函數的各參數自定義logging的輸出。
參數名稱
|
描述
|
filename
|
指定日誌輸出目標文件的文件名,指定該設置項後日志信心就不會被輸出到控制檯了
|
filemode
|
指定日誌文件的打開模式,默認爲'a'。須要注意的是,該選項要在filename指定時纔有效
|
format
|
指定日誌格式字符串,即指定日誌輸出時所包含的字段信息以及它們的順序。logging模塊定義的格式字段下面會列出。
|
datefmt
|
指定日期/時間格式。須要注意的是,該選項要在format中包含時間字段%(asctime)s時纔有效
|
level
|
指定日誌器的日誌級別
|
stream
|
指定日誌輸出目標stream,如sys.stdout、sys.stderr以及網絡stream。須要說明的是,stream和filename不能同時提供,不然會引起 ValueError異常
|
style
|
Python 3.2中新添加的配置項。指定format格式字符串的風格,可取值爲'%'、'{'和'$',默認爲'%'
|
handlers
|
Python 3.3中新添加的配置項。該選項若是被指定,它應該是一個建立了多個Handler的可迭代對象,這些handler將會被添加到root logger。須要說明的是:filename、stream和handlers這三個配置項只能有一個存在,不能同時出現2個或3個,不然會引起ValueError異常。
|
上表中的參數format能夠經過logging模塊中定義好模式來設定值:
字段/屬性名稱
|
使用格式
|
描述
|
asctime
|
%(asctime)s
|
將日誌的時間構形成可讀的形式,默認狀況下是‘2019-03-28 00:00:00,000’的形式,精確到毫秒
|
name
|
%(name)s
|
所使用的日誌器名稱,默認是'root',由於默認使用的是 rootLogger
|
filename
|
%(filename)s
|
調用日誌輸出函數的模塊的文件名; pathname的文件名部分,包含文件後綴
|
funcName
|
%(funcName)s
|
由哪一個function發出的log, 調用日誌輸出函數的函數名
|
levelname
|
%(levelname)s
|
日誌的最終等級(被filter修改後的)
|
message
|
%(message)s
|
日誌信息, 日誌記錄的文本內容
|
lineno
|
%(lineno)d
|
當前日誌的行號, 調用日誌輸出函數的語句所在的代碼行
|
levelno
|
%(levelno)s
|
該日誌記錄的數字形式的日誌級別(10, 20, 30, 40, 50)
|
pathname
|
%(pathname)s
|
完整路徑 ,調用日誌輸出函數的模塊的完整路徑名,可能沒有
|
process
|
%(process)s
|
當前進程, 進程ID。可能沒有
|
processName
|
%(processName)s
|
進程名稱,Python 3.1新增
|
thread
|
%(thread)s
|
當前線程, 線程ID。可能沒有
|
threadName
|
%(thread)s
|
線程名稱
|
module
|
%(module)s
|
調用日誌輸出函數的模塊名, filename的名稱部分,不包含後綴即不包含文件後綴的文件名
|
created
|
%(created)f
|
當前時間,用UNIX標準的表示時間的浮點數表示; 日誌事件發生的時間--時間戳,就是當時調用time.time()函數返回的值
|
relativeCreated
|
%(relativeCreated)d
|
輸出日誌信息時的,自Logger建立以 來的毫秒數; 日誌事件發生的時間相對於logging模塊加載時間的相對毫秒數
|
msecs
|
%(msecs)d
|
日誌事件發生事件的毫秒部分。logging.basicConfig()中用了參數datefmt,將會去掉asctime中產生的毫秒部分,能夠用這個加上
|
因此,結合上表中的內容,咱們能夠實現讓每一條日誌記錄輸出事件發生時間、事件發生位置、日誌級別、事件內容等信息。
如今,咱們來給剛纔的日誌添加一些輸出,例如每條日誌輸出日誌時間、日誌級別、所在模塊名、函數名、行號等信息,並指定時間輸出格式,最後把日誌輸出到當前目錄下的.log文件中。代碼以下:
import logging
fmt = '%(asctime)s , %(levelname)s , %(filename)s %(funcName)s line %(lineno)s , %(message)s'
datefmt = '%Y-%m-%d %H:%M:%S %a'
logging.basicConfig(level=logging.DEBUG,
format=fmt,
datefmt=datefmt,
filename=".log")
logging.debug('debug')
logging.info('info')
logging.warn('warn')
logging.error('error')
logging.critical('critical')
運行上述代碼後,控制檯不會再有輸出了,但當前目錄下的.log文件會寫入一下內容:
2019-03-28 16:34:08 Thu , DEBUG , log_test.py <module> line 8 , debug
2019-03-28 16:34:08 Thu , INFO , log_test.py <module> line 9 , info
2019-03-28 16:34:08 Thu , WARNING , log_test.py <module> line 10 , warn
2019-03-28 16:34:08 Thu , ERROR , log_test.py <module> line 11 , error
2019-03-28 16:34:08 Thu , CRITICAL , log_test.py <module> line 12 , critical
3.2 記錄日誌之logging四大組件
logging四大組件是logging日誌記錄的高級用法。四大組件包括Logger、Handelr、Filter、Formater,且都是以類的形式來使用。logging四大組件協同工做流以下如所示:
各組件功能以下:
組件名稱
|
對應類名
|
功能描述
|
日誌器
|
Logger
|
用於提供日誌接口,經常使用於配置和發送日誌消息
|
處理器
|
Handler
|
用於寫入日誌並輸出到指定位置,例如控制檯、文件或網絡位置等
|
過濾器
|
Filter
|
對日誌記錄進行進一步過濾,輸出符合條件的日誌記錄
|
格式器
|
Formatter
|
配置日誌記錄的最終輸出格式
|
(1)日誌器:Logger
日誌器Logger以工廠化的形式返回一個Logger類實例。通常而言,大多使用下面的方法得到Logger類實例:
logging.getLogger(name)
屬性name是爲Logger實例指定的名稱,若是使用同一個名稱進行實例化,則實際上只是將後面實例對象名指向前面的同名Logger實例。在使用logging模塊時,系統會自動實例化一個名爲root的日誌器(根日誌器),當未指定name屬性時,事實上就是將變量名指向跟日誌器
另外,
Logger實例具備層級繼承的特色,層級之間已「.」鏈接,例如:「a.b」,「a.b.c」,a是父日誌器,b是子日誌器,在未對子日誌器進行配置狀況下,子日誌器默認繼承父日誌器的配置,對子日誌器從新配置不會影響父日誌器。這一點很重要,在多模塊中記錄日子是可使用這一特性,咱們在下文代碼中實踐這一特性。根日誌器是全部日誌器的默認父日誌器。
Logger類還有如下的經常使用方法:
- logger.setLevel() :設置日誌器處理日誌信息的最低級別
- logger.addHandler():爲該logger對象添加一個handler對象
- logger.removeHandler():爲該logger對象添加移除一個handler對象
- logger.addFilter():爲該logger對象添加一個filter對象
- logger.removeFilter():爲該logger對象移除一個filter對象
- logger.debug(),logger.info(),logger.warning(),logger.error(),logger.critical():建立一個對應等級的日誌記錄
(2)處理器:Handler
Handler實例用於將日誌記錄發送到指定的位置進行輸出。一個logger對象能夠添加多個handler(例如既要在控制檯輸出日誌,又要將日誌寫入到文件A,還要講日誌寫入文件B,這就能夠配置3個handler),每一個handler又能夠定義不一樣日誌級別,以實現日誌分級過濾顯示。經常使用的方法包括:
- handler.setLevel():設置handler處理的日誌信息最低級別
- handler.setFormatter():爲handler設置一個格式器對象
- handler.addFilter():爲handler添加一個過濾器對象
- handler.removeFilter():爲handler刪除一個過濾器對象
要注意的是,在實際開發中,最好不要直接使用Handler類,應根據實際的功能須要,實例化Handler類的子類。Handler類的之類包括:
Handler
|
描述
|
logging.StreamHandler
|
將日誌消息發送到輸出到Stream,如std.out, std.err或任何file-like對象。
|
logging.FileHandler
|
將日誌消息發送到磁盤文件,默認狀況下文件大小會無限增加
|
logging.handlers.RotatingFileHandler
|
將日誌消息發送到磁盤文件,並支持日誌文件按大小切割
|
logging.hanlders.TimedRotatingFileHandler
|
將日誌消息發送到磁盤文件,並支持日誌文件按時間切割
|
logging.handlers.HTTPHandler
|
將日誌消息以GET或POST的方式發送給一個HTTP服務器
|
logging.handlers.SMTPHandler
|
將日誌消息發送給一個指定的email地址
|
logging.NullHandler
|
該Handler實例會忽略error messages,一般被想使用logging的library開發者使用來避免'No handlers could be found for logger XXX'信息的出現。
|
經過代碼來演示一下,功能以下:在控制檯輸出日誌(日誌級別爲debug),同時將日誌寫入到文件a.log文件(日誌級別爲debug),還要講日誌寫入文件b.log文件(日誌級別爲warn):
import logging
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
# 控制檯輸出
con_handler = logging.StreamHandler()
con_handler.setLevel(logging.INFO)
logger.addHandler(con_handler)
# 輸出到文件a.log
file_a_handler = logging.FileHandler('./a.log', encoding='UTF-8')
file_a_handler.setLevel(logging.DEBUG)
logger.addHandler(file_a_handler)
# 輸出到文件b.log
file_b_handler = logging.FileHandler('./b.log', encoding='UTF-8')
file_b_handler.setLevel(logging.WARNING)
logger.addHandler(file_b_handler)
if __name__=='__main__':
logger.debug('debug msg')
logger.info('info msg')
logger.warning('warn msg')
運行上面代碼後,控制檯輸出以下:
info msg
warn msg
文件a.log會寫入一下內容:
debug msg
info msg
warn msg
文件b.log會寫入如下內容:
warn msg
注意:在一個日誌器中添加多個handler時要注意,最好經過logger.setLevel(logging.DEBUG)先設置一下logger自己的日誌級別,若是某個handler的級別比logger的日誌級別低,那麼該handler的日誌級別無效,handler會以logger的級別來處理。
(3)格式器:Formatter
Formatter類實例用於配置日誌記錄的內容、結構等信息。能夠經過如下三個參數進行配置:
- fmt:指定消息格式化字符串,若是不指定該參數則默認使用message的原始值
- datefmt:指定日期格式字符串,若是不指定該參數則默認使用"%Y-%m-%d %H:%M:%S"
- style:指定格式化佔位符,可取值爲 '%', '{'和 '$',若是不指定該參數則默認使用'%'
fmt的使用方法能夠參照上文中介紹過的
logging.basicConfig函數format參數的配製方法。
例:每條日誌輸出日誌時間、日製定及、所在模塊名、函數名、行號等信息,並指定時間輸出格式,最後把日誌輸出到控制檯。代碼以下:
import logging
logger = logging.getLogger(__name__)
handler = logging.StreamHandler()
logger.setLevel(logging.DEBUG)
# 定義格式器,添加處處理器中
fmt = '%(asctime)s , %(levelname)s , %(filename)s %(funcName)s line %(lineno)s , %(message)s'
datefmt = '%Y-%m-%d %H:%M:%S %a'
log_fmt = logging.Formatter(fmt=fmt, datefmt=datefmt)
handler.setFormatter(log_fmt)
logger.addHandler(handler)
logger.debug('debug msg')
logger.info('info msg')
控制檯輸出以下:
2019-03-29 19:36:03 Fri , DEBUG , log_test2.py <module> line 14 , debug msg
2019-03-29 19:36:03 Fri , INFO , log_test2.py <module> line 15 , info msg
(4)過濾器:Filter
在咱們已經知道的logging使用方法中,都是經過日誌級別來控制日誌是否輸出,Filter可以實現更增強大的過濾功能,控制日誌輸出。自定義的過濾器中必須覆寫filter方法,當filter的返回值判斷爲True則容許輸出,反之不容許輸出。例如過濾包含敏感信息的日誌,過濾器定義以下:
import logging
class CountryFilter(logging.Filter):
def filter(self,record):
return "America" not in record.getMessage()
logger = logging.getLogger()
handler = logging.StreamHandler()
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
logger.addFilter(CountryFilter())
logger.critical('I love America')
logger.debug('I love China')
輸出結果:
I love China
能夠看到,雖然第一條日誌記錄的日誌等級更高,可是由於包含了過濾器中包含的敏感信息,因此不被容許輸出。
4 logging奇淫巧技
4.1 記錄異常信息:捕獲traceback
若是在日誌中,只是記錄發生了異常,那其實做用不大,若是traceback也記錄到日誌中,那就完美了。強大的logging確實也提供了這一功能,並且使用也很簡單:
import logging
logger = logging.getLogger(__name__)
handler = logging.FileHandler('./.log',encoding='utf-8')
logger.setLevel(logging.DEBUG)
# 定義格式器,添加處處理器中
fmt = '%(asctime)s , %(levelname)s , %(filename)s %(funcName)s line %(lineno)s , %(message)s'
datefmt = '%Y-%m-%d %H:%M:%S %a'
log_fmt = logging.Formatter(fmt=fmt, datefmt=datefmt)
handler.setFormatter(log_fmt)
logger.addHandler(handler)
try:
logger.info('Running …')
1/0
except Exception as e:
logger.error('Exception occurs!',exc_info = True)
# logger.exception(e) # 與上面這行效果同樣
運行後,文件.log會被寫入如下內容:
2019-03-29 19:53:14 Fri , INFO , log_test2.py <module> line 15 , Running …
2019-03-29 19:53:14 Fri , ERROR , log_test2.py <module> line 18 , Exception occurs!
Traceback (most recent call last):
File "E:/myCode/test1/log_test2.py", line 16, in <module>
1/0
ZeroDivisionError: division by zero
4.2 多模塊共享日誌
在開發過程當中,常常出現多個模塊都須要記錄日誌的狀況,也許你想到的作法是在一個模塊中配置好一個logger並實例化,在須要用到的模塊中進行導入,但若是不一樣模塊的日誌器配置有區別時,這種方法就不適用了,如果爲每一個模塊都定義一個logger,全部配置都須要從新寫入,有些繁瑣。還記得上文中提到logging的日誌器能夠經過name屬性進行分層嗎?子日誌器能夠繼承父日誌器的配置,也能夠從新配置,這就是logging給咱們提供的多模塊共享日誌的解決方案。看代碼:
模塊main.py中的代碼:
import logging
import log_child
logger = logging.getLogger('main')
logger.setLevel(logging.DEBUG)
fmt = '%(name)s , %(asctime)s , %(levelname)s , %(filename)s %(funcName)s line %(lineno)s , %(message)s'
datefmt = '%Y-%m-%d %H:%M:%S %a'
log_fmt = logging.Formatter(fmt=fmt, datefmt=datefmt)
handler = logging.FileHandler('./.log',encoding='utf-8')
handler.setFormatter(log_fmt)
logger.addHandler(handler)
if __name__=='__main__':
logger.debug('Running …')
log_child.fun_child()
模塊child_log.py中的代碼:
import logging
logger = logging.getLogger('main.child')
logger.setLevel(logging.DEBUG)
def fun_child():
try:
logger.info('Running …')
1 / 0
except Exception as e:
logger.exception(e)
運行main.py後,.log文件會被寫入一下內容:
main , 2019-03-29 20:23:32 Fri , DEBUG , main.py <module> line 16 , Running …
main.child , 2019-03-29 20:23:32 Fri , INFO , log_child.py fun_child line 7 , Running …
main.child , 2019-03-29 20:23:32 Fri , ERROR , log_child.py fun_child line 10 , division by zero
Traceback (most recent call last):
File "E:\myCode\test1\log_child.py", line 8, in fun_child
1 / 0
ZeroDivisionError: division by zero
4.3 使用配置文件配置logger
咱們以前的程序中都是將對logger的配置一併寫在程序中,但事實上,採用配置化編程的方式,將對logger的配置寫在專門的配置文件中,例如寫入json文件、conf文件、yaml文件等文件中,當須要實例化logger時,讀取便可。下面以conf文件爲例,經過代碼註釋的方式,介紹logging配置文件的書寫方式。配置文件log.conf以下:
[loggers] #固定寫法,定義logger的模塊
keys=root,log_1,log_2 #建立三個logger,root是父類,必需存在的,其餘兩個logger的name分別爲
[logger_root] # 定製上面的logger,嚴格要求格式爲"logger_loggername",必須經過loggername與上面的logger一一對應
level=DEBUG # 設置日誌級別
qualname=root # 對於root,其實這裏能夠不填,默認就是root
handlers=debugs #設置指定處理器,若是有多個處理器,中間以逗號分隔,這個名字待會兒 咱們會以固定格式"handler_(value)"建立
[logger_log_1]
level=INFO
qualname=log_1 #除了root之外,必需要設置這個屬性,用於定義打印輸出時候的logger名
handlers=infos
propagate=0 # 是否將消息想父日誌傳遞,0表示不傳遞,1表示傳遞。若是向上傳遞,父日誌器接收到消息後會以父日誌器的配置再次處理該消息,因此可能全部屢次輸出
[logger_log_2]
level=WARNING
qualname=log_2
handlers=warns
[handlers] #固定格式, 開始定義處理器
keys=debugs,infos,warns#定義過濾器名稱,與上面出現的handlers的值一一對應,下面定義以handler_handlername格式定義
[handler_debugs]
class=StreamHandler # 指定處理器的類名
level=DEBUG # 設置級別
formatter=form01 #定義格式器,名稱爲form01,下面會建立formatters,格式也是嚴格要求爲formatter_formattername
args=(sys.stdout,) # 控制檯輸出
[handler_infos]
class=FileHandler
level=INFO
formatter=form02
args=('b.log','a')
[handler_warns]
class=FileHandler
level=WARNING
formatter=form02
args=('a.log','a')# 寫入到文件,寫入方式
[formatters] #固定格式
keys=form01,form02 #定義名稱,下面會引用格式,與上面出現的formatter的值對應
[formatter_form01]
format=%(asctime)s %(message)s # 定義消息輸出格式,內容
datefmt=%Y-%m-%d %H:%M:%S #日期輸出格式
[formatter_form02]
format=%(asctime)s %(filename)s %(levelname)s %(message)s
datefmt=%Y-%m-%d %H:%M:%S
實例化logger:
# _*_coding:utf-8_*_
import logging
from logging.config import fileConfig
fileConfig('log.conf')
root= logging.getLogger(name="root")
log_1= logging.getLogger(name="log_1")
log_2= logging.getLogger(name="log_2")
root.debug('root_debug')
root.info('root_info')
root.warning('root_warning')
log_1.debug('log_1_debug')
log_1.info('log_1_info')
log_1.warning('log_1_warning')
log_2.debug('log_2_debug')
log_2.info('log_2_info')
log_2.warning('log_2_warning')
程序運行後,控制檯輸出以下:
2019-03-29 21:43:24 root_debug
2019-03-29 21:43:24 root_info
2019-03-29 21:43:24 root_warning
a.log文件將被寫入如下內容:
2019-03-29 21:43:24 main.py INFO log_1_info
2019-03-29 21:43:24 main.py WARNING log_1_warning
b.log文件將被寫入如下內容:
2019-03-29 21:43:24 main.py WARNING log_2_warning
4.3 日誌回滾
什麼是日誌回滾呢?咋一聽,好像不知道是什麼東西。日誌回滾就是按照日期或者時間(有時候甚至是日誌和時間綜合做用),對日誌進行分割或者刪除。實際開發中常常須要用到,由於隨着應用的持續運行,日誌文件會愈來愈龐大,對系統的性能產生影響,因此有必要刪除早起的日誌。
logging中提供了兩個處理器用於日誌回滾,一個是RotatingFileHandler,它主要是根據日誌文件的大小進行滾動,另外一個是TimeRotatingFileHandler,它主要是根據時間進行滾動。
(1)根據文件大小進行回滾
按文件大小回滾的類是RotatingFileHandler:
# -*- coding:utf-8 -*-
import logging
from logging.handlers import RotatingFileHandler
logger = logging.getLogger('main')
logger.setLevel(level = logging.INFO)
# 定義一個RotatingFileHandler,最多備份三個日誌文件, 每一個日誌文件最大1k
file_handler = RotatingFileHandler(".log",maxBytes = 1*1024,backupCount = 3)
file_handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)
cons_handler = logging.StreamHandler()
cons_handler.setLevel(logging.DEBUG)
cons_handler.setFormatter(formatter)
logger.addHandler(file_handler)
logger.addHandler(cons_handler)
if __name__=='__main__':
while True:
logger.debug("debug")
logger.info("info")
logger.warning("warning")
logger.critical("critical")
上述程序執行後,將持續在控制檯輸出全部的日誌記錄,日誌記錄文件有三個,循環向日志文件中寫入日誌,當文件大小達到1kb時,開始在另外一個文件刪除日誌記錄,並寫入新的日誌記錄。
(2)根據時間進行回滾。
按文件時間回滾的類時TimeRotatingFileHandler,這一個類包含如下參數:
filename :輸出日誌文件名的前綴,好比main.log
when 是一個字符串的定義以下:
「S」: Seconds
「M」: Minutes
「H」: Hours
「D」: Days
「W」: Week day (0=Monday)
「midnight」: Roll over at midnight
interval 是指等待多少個單位when的時間後
import time
import logging
import logging.handlers
# logging初始化工做
logging.basicConfig()
# logger的初始化工做
logger = logging.getLogger('main')
logger.setLevel(logging.INFO)
# 添加TimedRotatingFileHandler
# 定義一個1秒換一次log文件的handler
# 保留3箇舊log文件
timefilehandler = logging.handlers.TimedRotatingFileHandler(".log", when='S', interval=1, backupCount=3)
# 設置後綴名稱,跟strftime的格式同樣
timefilehandler.suffix = "%Y-%m-%d_%H-%M-%S.log"
formatter = logging.Formatter('%(asctime)s|%(name)-12s: %(levelname)-8s %(message)s')
timefilehandler.setFormatter(formatter)
logger.addHandler(timefilehandler)
while True:
time.sleep(0.1)
logger.debug("debug")
logger.info("info")
logger.warning("warning")
logger.critical("critical")
5 總結
本篇系統得總結了Python內容的日誌記錄模塊logging的用法,囊括了logging的大部份內容。掌握本篇內容,感受在開發中基本沒有問題。
參考: