1. 日志模塊簡(jiǎn)介
運(yùn)維工作有很多情況需要查問題、解決bug负蚊,而查問題和解決bug的過程離不開查看日志擂煞,我們編寫腳本或程序時(shí)總是需要有日志輸出拳昌,Python的logging模塊就是為記錄日志使用的,而且是線程安全的钳垮,意味著使用它完全不用擔(dān)心因日志模塊的異常導(dǎo)致程序崩潰惑淳。
將日志打印到屏幕:
import logging
logging.debug('debug message')
logging.info('info message')
logging.warning('warning message')
logging.error('error message')
logging.critical('critical message')
輸出為:
WARNING:root:warning message
ERROR:root:error message
CRITICAL:root:critical message
默認(rèn)情況下,Python的logging模塊將日志打印到標(biāo)準(zhǔn)輸出中扔枫,而且只顯示大于等于WARNING級(jí)別的日志汛聚,這說明默認(rèn)的日志級(jí)別設(shè)置為WARNING(日志級(jí)別等級(jí)CRITICAL>ERROR>WARNING>INFO>DEBUG)
默認(rèn)的日志格式:日志級(jí)別為L(zhǎng)ogger,名稱為用戶輸出消息短荐。
各日志級(jí)別代表的含義如下:
- DEBUG:調(diào)試時(shí)的信息打印倚舀。
- INFO:正常的日志信息記錄叹哭。
- WARNING:發(fā)生了警告信息,但程序仍能正常工作痕貌。
- ERROR:發(fā)生了錯(cuò)誤风罩,部分功能已不正常。
- CRITICAL:發(fā)生嚴(yán)重錯(cuò)誤舵稠,程序可能已崩潰超升。
將日志信息記錄至文件(文件名:lx_log1.py):
import logging
logging.basicConfig(filename='./lx_log1.log')
logging.debug('debug message')
logging.info('info message')
logging.warning('warning message')
logging.error('error message')
logging.critical('critical message')
執(zhí)行以上代碼后發(fā)現(xiàn),在當(dāng)前目錄多了一個(gè)文件ls_log1.log哺徊,文件內(nèi)容與第一個(gè)例子的輸出是一致的室琢。多次執(zhí)行l(wèi)x_log1.py發(fā)現(xiàn)log文件的內(nèi)容變多了,說明默認(rèn)的寫log文件的方式是追加落追。
2. logging模塊的配置與使用
我們可以通過logging模塊的配置改變log文件的寫入方式盈滴、日志級(jí)別、時(shí)間戳等信息轿钠。
logging.basicConfig(level=logging.DEBUG, ---設(shè)置日志的級(jí)別
format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s', ---日志的格式
datefmt='%Y-%m-%d &H:%M:%S', ---時(shí)間格式
filename='./lx_log1.log', ---指定文件位置
filemode='w') ---指定寫入方式
可見在logging.basicConfig()函數(shù)中可通過具體參數(shù)來更改logging模塊的默認(rèn)行為巢钓。
- filename:用指定的文件名創(chuàng)建FileHandler,這樣日志會(huì)被存儲(chǔ)在指定的文件中疗垛。
- filemode:文件打開方式症汹,在指定了filename時(shí)使用這個(gè)參數(shù),默認(rèn)值為a贷腕,還可指定為w背镇。
- format:指定handler使用的日志顯示格式。
- datefmt:指定日期時(shí)間格式花履。
- level:設(shè)置rootlogger的日志級(jí)別芽世。
- stream:用指定的stream創(chuàng)建StreamHandler」畋冢可以指定輸出到sys.stdout或者文件济瓢,默認(rèn)為sys.stderr。若同時(shí)列出了filename和stream兩個(gè)參數(shù)妹卿,則stream參數(shù)會(huì)被忽略旺矾。
format參數(shù)中可能用到的格式化串如下:
- %(name)s Logger的名字。
- %(levelno)s 數(shù)字形式的日志級(jí)別夺克。
- %(levelname)s 文本形式的日志級(jí)別箕宙。
- %(pathname)s 調(diào)用日志輸出函數(shù)的模塊的完整路徑名,可能沒有铺纽。
- %(filename)s 調(diào)用日志輸出函數(shù)的模塊的文件名柬帕。
- %(module)s 調(diào)用日志輸出函數(shù)的模塊名。
- %(funcName)s 調(diào)用日志輸出函數(shù)的函數(shù)名。
- %(lineno)d 調(diào)用日志輸出函數(shù)的語(yǔ)句所在的代碼行陷寝。
- %(created)f 當(dāng)前時(shí)間锅很,用UNIX標(biāo)準(zhǔn)表示時(shí)間的浮點(diǎn)數(shù)。
- %(relativeCreated)d 輸出日志信息時(shí)凤跑,自logger創(chuàng)建以來的毫秒數(shù)爆安。
- %(asctime)s 字符串形式的當(dāng)前時(shí)間。默認(rèn)格式是“2020-02-14 16:14:52,896”仔引。逗號(hào)后面的是毫秒扔仓。
- %(thread)d 線程ID,可能沒有咖耘。
- %(threadName)s 線程名翘簇,可能沒有。
- %(process)d 進(jìn)程ID鲤看,可能沒有缘揪。
- %(message)s 用戶輸出的消息。
import logging
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
filename='./lx_log1.log',
filemode='w',
)
logging.debug('debug message')
logging.info('info message')
logging.warning('warning message')
logging.error('error message')
logging.critical('critical message')
運(yùn)行代碼后我們會(huì)看到lx_log1.log文件的內(nèi)容如下:
2020-02-15 01:58:20 lx_log1.py[line:9] DEBUG debug message
2020-02-15 01:58:20 lx_log1.py[line:10] INFO info message
2020-02-15 01:58:20 lx_log1.py[line:11] WARNING warning message
2020-02-15 01:58:20 lx_log1.py[line:12] ERROR error message
2020-02-15 01:58:20 lx_log1.py[line:13] CRITICAL critical message
這樣的配置已基本滿足我們歇寫一些小程序或Python腳本的日志需求义桂。然而這還不夠體現(xiàn)logging模塊的強(qiáng)大,畢竟以上功能通過自定義一個(gè)函數(shù)也可以方便實(shí)現(xiàn)蹈垢。下面的是幾個(gè)概念以及他們之間的關(guān)系圖慷吊。
- logger:記錄器,應(yīng)用程序代碼能直接使用的接口曹抬。
- handler:處理器溉瓶,將(記錄器產(chǎn)生的)日志記錄發(fā)送至合適的目的地。
- filter:過濾器谤民,提供了更好的粒度控制堰酿,可以決定輸出哪些日志記錄。
- formatter:格式化器张足,指明了最終輸出中日志記錄的布局触创。
日志事件信息在記錄器(logger)、處理器(handler)为牍、過濾器(filter)哼绑、格式化器(formatter)之間通過一個(gè)日志記錄實(shí)例來傳遞。通過調(diào)用記錄器實(shí)例的方法來記錄日志碉咆,每一個(gè)記錄器實(shí)例都有一個(gè)名字抖韩,名字相當(dāng)于其命名空間,是一個(gè)樹狀結(jié)構(gòu)疫铜。例如茂浮,一個(gè)記錄器叫scan,記錄器scan.tex、scan.html席揽、scan.pdf的夫節(jié)點(diǎn)顽馋。記錄器的名稱【阅幔可以任意取趣避,但一個(gè)比較好的實(shí)踐是通過下面的方式來命名一個(gè)記錄器。
logger = logging.getLogger(__name__)
上面這條語(yǔ)句意味著記錄器的名字會(huì)通過搜索包的層級(jí)來獲致新翎,根記錄器叫root logger程帕。記錄器通過debug()、info()地啰、warning()愁拭、error()和critical()方法記錄相應(yīng)級(jí)別的日志,根記錄器也一樣亏吝。
根記錄器root logger輸出的名稱是'root'岭埠。當(dāng)然,日志的輸出位置可能是不同的蔚鸥,logging模塊支持將日志信息輸出到終端惜论、文件、HTTP GET/POST請(qǐng)求止喷、郵件馆类、網(wǎng)絡(luò)sockets、隊(duì)列或操作系統(tǒng)級(jí)的日志等弹谁。日志的輸出位置在處理器handler類中進(jìn)行配置乾巧,如果內(nèi)建的handler類無法滿足需求,則可以自定義handler類來實(shí)現(xiàn)自己特殊的需求预愤。默認(rèn)情況下沟于,日志的輸出位置為終端(標(biāo)準(zhǔn)錯(cuò)誤輸出),可以通過logging模塊的basicConfig()方法指定一個(gè)具體的位置來輸出日志植康,如終端或文件旷太。
logger和handler的工作流程如下:
現(xiàn)在讓我們從整體到局部來說明logger的日志記錄過程。
第一步:獲取logger的名稱向图。
logger = logging.getLogger('logger name') ---這里的logger name是自己定義的
第二步:配置logger泳秀。
1)配置該logger的輸出級(jí)別,如logger.setLevel(loging.INFO)榄攀。
2)添加該logger的輸出位置嗜傅,即logger的handler,logger.addHandler(ch)檩赢。這里的ch是我們自定義的handler吕嘀,如ch=logging.StreamHandler违寞,即輸出到終端。我們可以添加多個(gè)handler偶房,一次性將日志輸出到不同的位置趁曼。日志的輸出格式是在handler中進(jìn)行配置,如ch.setFormatter(formatter)棕洋,formatter也是我們自定義的挡闰,如formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')。不同的handler可以配置不同的格式化器掰盘,可以實(shí)現(xiàn)不同的輸出位置摄悯,不同的輸出格式,完全可能靈活配置愧捕。
第三步:在應(yīng)用程序中記錄日志奢驯。
logger.debug('debug message')
logger.info('info message')
logger.warning('warning message')
logger.error('error message')
logger.critical('critical message')
將日志信息顯示在終端的同時(shí)也在文件中記錄(lx_log2.py)。
# -*- coding: utf-8 -*-
import logging
# 創(chuàng)建logger次绘,其名稱為aimple_example瘪阁,名稱為任意,也可為空
logger = logging.getLogger("simple_example")
# 打印logger的名稱
print(logger.name)
# 設(shè)置logger的日志級(jí)別
logger.setLevel(logger.INFO)
# 創(chuàng)建兩個(gè)handler邮偎,一個(gè)負(fù)責(zé)將日志輸出到終端管跺,一個(gè)負(fù)責(zé)輸出到文件,并分別設(shè)置它們的日志級(jí)別
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
fh = logging.FileHandler(filename="simple.log", mode="a", encoding="utf*8")
fh.setLevel(logging.WARNING)
# 創(chuàng)建一個(gè)格式化器禾进,可以創(chuàng)建不同的格式化器用于不同的handler伙菜,這里我們使用一個(gè)
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
# 設(shè)置兩個(gè)handler的格式化器
ch.setFormatter(formatter)
fh.setFormatter(formatter)
# 為logger添加兩個(gè)handler
logger.addHandler(ch)
logger.addHandler(fh)
# 在程序中記錄日志
logger.debug("debug message")
logger.info("info message")
logger.warning("warning message")
logger.error("error message")
logger.critical("critical message")
在以上程序中我們?cè)O(shè)置了logger的日志級(jí)別為INFO,handler ch的日志級(jí)別為DEBUG命迈,handler fh的日志級(jí)別為WARNING,這樣做是為了解釋它們之前的優(yōu)先級(jí)火的。
handler的日志級(jí)別以logger的日志為基礎(chǔ)壶愤,logger的日志級(jí)別為INFO,低于INFO級(jí)別的(如DEBUG)均不會(huì)在handler中出現(xiàn)馏鹤。handler中的日志級(jí)別如果高于logger征椒,則只顯示更高級(jí)別的日志信息,如fh應(yīng)該只顯示W(wǎng)ARNING及以上的日志信息湃累;handler中的日志級(jí)別如果低于或等于logger的日志級(jí)別勃救,則顯示logger的日志級(jí)別及以上信息,如ch應(yīng)該顯示INFO及以上的日志信息治力。
執(zhí)行python lx_log2.py得到如下結(jié)果:
simple_example
2020-02-15 14:54:29,459 - simple_example - INFO - info message
2020-02-15 14:54:29,459 - simple_example - WARNING - warning message
2020-02-15 14:54:29,459 - simple_example - ERROR - error message
2020-02-15 14:54:29,459 - simple_example - CRITICAL - critical message
查看simple.log文件蒙秒,內(nèi)容如下:
2020-02-15 14:54:29,459 - simple_example - WARNING - warning message
2020-02-15 14:54:29,459 - simple_example - ERROR - error message
2020-02-15 14:54:29,459 - simple_example - CRITICAL - critical message
從運(yùn)行結(jié)果來看,符合我們的預(yù)期宵统。除了StreamHandler和FileHandler外晕讲,logging模塊還提供了其他更為實(shí)用的Handler子類,它們都繼承在Handler基類,如下所示瓢省。
- BaseRotatingHandler:是循環(huán)日志處理器的基類弄息,不能直接被實(shí)例化,可使用RotatingFileHandler和TimeRotatingFileHandler勤婚。
- RotatingFileHandler:將日志文件記錄至磁盤文件摹量,可以設(shè)置每個(gè)日志文件的最大占用空間。
- TimeRotatingFileHandler:將日志文件記錄至磁盤文件馒胆,按固定的時(shí)間間隔來循環(huán)記錄日志缨称。
- SocketHandler:可以將日志信息發(fā)送到TCP/IP套接字。
- DatagramHandler:可以將日志信息發(fā)送到UDP套接字国章。
- SMTPHandler:可以將日志文件發(fā)送至郵箱具钥。
- SysLogHandler:系統(tǒng)日志處理器,可以將日志文件發(fā)送至UNIX系統(tǒng)日志液兽,也可以是一個(gè)遠(yuǎn)程機(jī)器骂删。
- NTEventLogHandler:Windows系統(tǒng)事件日志處理器,可以將日志文件發(fā)送到Windows系統(tǒng)事件日志四啰。
- MemoryHandler:MemoryHandler實(shí)例向內(nèi)存中的緩沖區(qū)發(fā)送消息宁玫,只要滿足特定的條件,緩沖區(qū)就會(huì)被刷新柑晒。
- HTTPHandlerL:使用GET或POST方法向HTTP服務(wù)器發(fā)送消息欧瘪。
- WatchedFileHandler:WatchedFileHandler實(shí)例監(jiān)視它們登錄到的文件。如果文件發(fā)生更改匙赞,則使用文件名關(guān)閉并重新打開佛掖。這個(gè)處理器只適用于類unix系統(tǒng),Windows不支持使用的底層機(jī)制涌庭。
- QueueHandler:QueueHandler實(shí)例向隊(duì)列發(fā)送消息芥被,比如在隊(duì)列或多處理模塊中實(shí)現(xiàn)的消息。
- NullHandler:NullHandler實(shí)例不使用錯(cuò)誤消息坐榆。庫(kù)開發(fā)人員使用日志記錄拴魄,但希望避免在庫(kù)用戶未配置日志記錄時(shí)顯示“日志記錄器XXX無法找到任何處理程序”消息。
日志的配置信息也可以來源于配置文件(lx_log3.py):
import logging
import logging.config
logging.config.fileConfig('logging.conf')
# 創(chuàng)建一個(gè)logger
logger = logging.getLogger('simpleExample')
# 日志記錄
logger.debug("debug message")
logger.info("info message")
logger.warning("warning message")
logger.error("error message")
logger.critical("critical message")
下面是配置文件的信息logging.conf:
[loggers]
keys=root,simpleExample
[handlers]
keys=fileHandler,consoleHandler
[formatters]
keys=simpleFormatter
[logger_root]
level=DEBUG
handlers=fileHandler
[logger_simpleExample]
level=DEBUG
handlers=consoleHandler
qualname=simpleExample
propagate=0
[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)
[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=%Y-%m-%d %H:%M:%S
上面幾種常用的方法已經(jīng)基本滿足我們的需求席镀。