Python自动化运维之常用模块—logging
??? 在現(xiàn)實生活中,記錄日志非常重要。銀行轉(zhuǎn)賬時會有轉(zhuǎn)賬記錄;如果有出現(xiàn)什么問題,人們可以通過日志數(shù)據(jù)來搞清楚到底發(fā)生了什么。
????對于系統(tǒng)開發(fā)、調(diào)試以及運行,記錄日志都是同樣的重要。如果沒有日志記錄,程序崩潰時你幾乎就沒辦法弄明白到底發(fā)生了什么事情。
1、簡單使用
執(zhí)行結(jié)果:
默認情況下,logging模塊將日志打印到屏幕上(stdout),日志級別為WARNING(即只有日志級別高于WARNING的日志信息才會輸出),日志格式如下圖所示:
1.1 日志級別
級別 | 數(shù)字形式 | 何時使用 |
DEBUG | 10 | 詳細信息,典型地調(diào)試問題時會感興趣。 |
INFO | 20 | 證明事情按預(yù)期工作。 |
WARNING | 30 | 表明發(fā)生了一些意外,或者不久的將來會發(fā)生問題(如‘磁盤滿了’)。軟件還是在正常工作。 |
ERROR | 40 | 由于更嚴重的問題 |
CRITICAL | 50 | 嚴重錯誤,表明軟件已,軟件已不能執(zhí)行一些功能了。不能繼續(xù)運行了。 |
NOSET | 0 | getattr(日志、記錄級。upper()) |
1.2 簡單配置
執(zhí)行結(jié)果:
標(biāo)準(zhǔn)輸出(屏幕)未顯示任何信息,發(fā)現(xiàn)當(dāng)前工作目錄下生成了logger.log,內(nèi)容如下:
因為通過level=logging.INFO設(shè)置日志級別為INFO,所以所有的日志信息均輸出出來了。
2、重要的概念
Logger ?? ??? ? 記錄器,暴露了應(yīng)用程序代碼能直接使用的接口。
Handler ?? ???? 處理器,將(記錄器產(chǎn)生的)日志記錄發(fā)送至合適的目的地。
Filter ?? ??? ???? 過濾器,提供了更好的粒度控制,它可以決定輸出哪些日志記錄。
Formatter ?? ?格式化器,指明了最終輸出中日志記錄的布局。
1.2.1 Logger 記錄器
Logger是一個樹形層級結(jié)構(gòu),在使用接口debug,info,warn,error,critical之前必須創(chuàng)建Logger實例,即創(chuàng)建一個記錄器,如果沒有顯式的進行創(chuàng)建,則默認創(chuàng)建一個root logger,并應(yīng)用默認的日志級別(WARN),處理器Handler(StreamHandler,即將日志信息打印輸出在標(biāo)準(zhǔn)輸出上),和格式化器Formatter(默認的格式即為第一個簡單使用程序中輸出的格式)。
創(chuàng)建Logger實例后,可以使用以下方法進行日志級別設(shè)置,增加處理器Handler。
logger.setLevel(logging.ERROR) # 設(shè)置日志級別為ERROR,即只有日志級別大于等于ERROR的日志才會輸出
2.2 Handler 處理器
Handler處理器類型有很多種,比較常用的有三個,StreamHandler,FileHandler,NullHandler,詳情可以訪問Python logging.handlers
創(chuàng)建StreamHandler之后,可以通過使用以下方法設(shè)置日志級別,設(shè)置格式化器Formatter,增加或刪除過濾器Filter。
StreamHandler
FileHandler
NullHandler
NullHandler類位于核心logging包,不做任何的格式化或者輸出。本質(zhì)上它是個“什么都不做”的handler,由庫開發(fā)者使用。
2.3 Formatter 格式化器
使用Formatter對象設(shè)置日志信息最后的規(guī)則、結(jié)構(gòu)和內(nèi)容,默認的時間格式為%Y-%m-%d %H:%M:%S。
其中,fmt是消息的格式化字符串,datefmt是日期字符串。如果不指明fmt,將使用'%(message)s'。如果不指明datefmt,將使用ISO8601日期格式。
2.4 Filter 過濾器
Handlers和Loggers可以使用Filters來完成比級別更復(fù)雜的過濾。Filter基類只允許特定Logger層次以下的事件。例如用‘A.B’初始化的Filter允許Logger ‘A.B’, ‘A.B.C’, ‘A.B.C.D’, ‘A.B.D’等記錄的事件,logger‘A.BB’, ‘B.A.B’ 等就不行。 如果用空字符串來初始化,所有的事件都接受。
以下是相關(guān)概念總結(jié):
熟悉了這些概念之后,有另外一個比較重要的事情必須清楚,即Logger是一個樹形層級結(jié)構(gòu);Logger可以包含一個或多個Handler和Filter,即Logger與Handler或Fitler是一對多的關(guān)系;
一個Logger實例可以新增多個Handler,一個Handler可以新增多個格式化器或多個過濾器,而且日志級別將會繼承。
?
3、Logging工作流程
第一次導(dǎo)入logging模塊或使用reload函數(shù)重新導(dǎo)入logging模塊,logging模塊中的代碼將被執(zhí)行,這個過程中將產(chǎn)生logging日志系統(tǒng)的默認配置。
自定義配置(可選)。logging標(biāo)準(zhǔn)模塊支持三種配置方式: dictConfig,fileConfig,listen。其中,dictConfig是通過一個字典進行配置Logger,Handler,Filter,Formatter;fileConfig則是通過一個文件進行配置;而listen則監(jiān)聽一個網(wǎng)絡(luò)端口,通過接收網(wǎng)絡(luò)數(shù)據(jù)來進行配置。當(dāng)然,除了以上集體化配置外,也可以直接調(diào)用Logger,Handler等對象中的方法在代碼中來顯式配置。
使用logging模塊的全局作用域中的getLogger函數(shù)來得到一個Logger對象實例(其參數(shù)即是一個字符串,表示Logger對象實例的名字,即通過該名字來得到相應(yīng)的Logger對象實例)。
使用Logger對象中的debug,info,error,warn,critical等方法記錄日志信息。
4、logging模塊使用過程
4.1 logging模塊處理流程
判斷日志的等級是否大于Logger對象的等級,如果大于,則往下執(zhí)行,否則,流程結(jié)束。
產(chǎn)生日志。第一步,判斷是否有異常,如果有,則添加異常信息。第二步,處理日志記錄方法(如debug,info等)中的占位符,即一般的字符串格式化處理。
使用注冊到Logger對象中的Filters進行過濾。如果有多個過濾器,則依次過濾;只要有一個過濾器返回假,則過濾結(jié)束,且該日志信息將丟棄,不再處理,而處理流程也至此結(jié)束。否則,處理流程往下執(zhí)行。
在當(dāng)前Logger對象中查找Handlers,如果找不到任何Handler,則往上到該Logger對象的父Logger中查找;如果找到一個或多個Handler,則依次用Handler來處理日志信息。但在每個Handler處理日志信息過程中,會首先判斷日志信息的等級是否大于該Handler的等級,如果大于,則往下執(zhí)行(由Logger對象進入Handler對象中),否則,處理流程結(jié)束。
執(zhí)行Handler對象中的filter方法,該方法會依次執(zhí)行注冊到該Handler對象中的Filter。如果有一個Filter判斷該日志信息為假,則此后的所有Filter都不再執(zhí)行,而直接將該日志信息丟棄,處理流程結(jié)束。
?使用Formatter類格式化最終的輸出結(jié)果。 注:Formatter同上述第2步的字符串格式化不同,它會添加額外的信息,比如日志產(chǎn)生的時間,產(chǎn)生日志的源代碼所在的源文件的路徑等等。
真正地輸出日志信息(到網(wǎng)絡(luò),文件,終端,郵件等)。至于輸出到哪個目的地,由Handler的種類來決定。
注:以上內(nèi)容摘抄自第三條參考資料,內(nèi)容略有改動,轉(zhuǎn)載特此聲明。
5、日志配置
5.1 配置方式
顯式創(chuàng)建記錄器Logger、處理器Handler和格式化器Formatter,并進行相關(guān)設(shè)置;
通過簡單方式進行配置,使用basicConfig()函數(shù)直接進行配置;
通過配置文件進行配置,使用fileConfig()函數(shù)讀取配置文件;
通過配置字典進行配置,使用dictConfig()函數(shù)讀取配置信息;
通過網(wǎng)絡(luò)進行配置,使用listen()函數(shù)進行網(wǎng)絡(luò)配置。
5.2 basicConfig關(guān)鍵字參數(shù)
關(guān)鍵字 | 描述 |
filename | 創(chuàng)建一個FileHandler,使用指定的文件名,而不是使用StreamHandler。 |
filemode | 如果指明了文件名,指明打開文件的模式(如果沒有指明filemode,默認為'a')。 |
format | handler使用指明的格式化字符串。 |
datefmt | 使用指明的日期/時間格式。 |
level | 指明根logger的級別。 |
stream | 使用指明的流來初始化StreamHandler。該參數(shù)與'filename'不兼容,如果兩個都有,'stream'被忽略。 |
5.3 有用的format格式
格式 | 描述 |
%(name)s | Logger的名字 |
%(levelno)s | 數(shù)字形式的日志級別 |
%(levelname)s | 文本形式的日志級別 |
%(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ù)的語句所在的代碼行 |
%(created)f | 當(dāng)前時間,用UNIX標(biāo)準(zhǔn)的表示時間的浮 點數(shù)表示 |
%(relativeCreated)d | 輸出日志信息時的,自Logger創(chuàng)建以 來的毫秒數(shù) |
%(asctime)s | 字符串形式的當(dāng)前時間。默認格式是 “2003-07-08 16:49:45,896”。逗號后面的是毫秒 |
%(thread)d | 線程ID??赡軟]有 |
%(threadName)s | 線程名。可能沒有 |
%(process)d | 進程ID??赡軟]有 |
%(message)s | 用戶輸出的消息 |
5.4 配置示例
5.4.1 顯式配置
使用程序logger.py如下:
5.4.2 文件配置
配置文件logging.conf如下:
使用程序logger.py如下:
5.4.3 字典配置
5.4.4 監(jiān)聽配置
可以使用logging.config.listen(port=DEFAULT_LOGGING_CONFIG_PORT)進行完善本文。
5.4.5 JSON配置
配置文件logging.json如下:
使用程序logger.py如下:
5.4.6 YAML配置
配置文件logging.yaml如下:
使用程序logger.py如下:
6、使用 __name__ 作為 logger 的名稱
雖然不是非得將 logger 的名稱設(shè)置為 __name__ ,但是這樣做會給我們帶來諸多益處。在 python 中,變量 __name__ 的名稱就是當(dāng)前模塊的名稱。比如,在模塊 “foo.bar.my_module” 中調(diào)用 logger.getLogger(__name__) 等價于調(diào)用logger.getLogger(“foo.bar.my_module”) 。當(dāng)你需要配置 logger 時,你可以配置到 “foo” 中,這樣包 foo 中的所有模塊都會使用相同的配置。當(dāng)你在讀日志文件的時候,你就能夠明白消息到底來自于哪一個模塊。
7、捕捉異常并使用 traceback 記錄它
出問題的時候記錄下來是個好習(xí)慣,但是如果沒有 traceback ,那么它一點兒用也沒有。你應(yīng)該捕獲異常并用 traceback 把它們記錄下來。比如下面這個例子:
使用參數(shù) exc_info=true 調(diào)用 logger 方法, traceback 會輸出到 logger 中。你可以看到下面的結(jié)果:
你也可以調(diào)用 logger.exception(msg, _args),它等價于 logger.error(msg, exc_info=True, _args)。
千萬不要在模塊層次獲取 Logger,除非 disable_existing_loggers 被設(shè)置為 False
你可以看到很多在模塊層次獲取 logger 的例子(在這篇文章我也使用了很多,但這僅僅為了讓示例更短一些)。它們看上去沒什么壞處,但事實上,這兒是有陷阱的 – 如果你像這樣在模塊中使用 Logger,Python 會保留從文件中讀入配置前所有創(chuàng)建的所有 logger。
my_module.py
本應(yīng)該在日志中看到記錄,但是你卻什么也沒有看到。為什么呢?這就是因為你在模塊層次創(chuàng)建了 logger,然后你又在加載日志配置文件之前就導(dǎo)入了模塊。logging.fileConfig 與 logging.dictConfig 默認情況下會使得已經(jīng)存在的 logger 失效。所以,這些配置信息不會應(yīng)用到你的 Logger 上。你最好只在你需要 logger 的時候才獲得它。反正創(chuàng)建或者取得 logger 的成本很低。你可以這樣寫你的代碼:
這樣,logger 就會在你加載配置后才會被創(chuàng)建。這樣配置信息就可以正常應(yīng)用。
python2.7 之后的版本中 fileConfg 與 dictConfig 都新添加了 “disable_existing_loggers” 參數(shù),將其設(shè)置為 False,上面提到的問題就可以解決了。例如:
8、使用旋轉(zhuǎn)文件句柄
如果你用 FileHandler 寫日志,文件的大小會隨著時間推移而不斷增大。最終有一天它會占滿你所有的磁盤空間。為了避免這種情況出現(xiàn),你可以在你的生成環(huán)境中使用 RotatingFileHandler 替代 FileHandler。
9、如果你有多個服務(wù)器可以啟用一個專用的日志服務(wù)器
當(dāng)你有多個服務(wù)器和不同的日志文件時,你可以創(chuàng)建一個集中式的日志系統(tǒng)來收集重要的(大多數(shù)情況是警告或者錯誤消息)信息。然后通過監(jiān)測這些日志信息,你就可以很容易地發(fā)現(xiàn)系統(tǒng)中的問題了。
10、總結(jié)
Python 的日志庫設(shè)計得如此之好,真是讓人欣慰,我覺得這是標(biāo)準(zhǔn)庫中最好的一部分了,你不得不選擇它。它很靈活,你可以用你自己的 handler 或者 filter。已經(jīng)有很多的第三方的 handler 了,比如 pyzmq 提供的 ZeroMQ 日志句柄,它允許你通過 zmq 套接字發(fā)送日志消息。如果你還不知道怎么正確的使用日志系統(tǒng),這篇文章將會非常有用。有了很好的日志記錄實踐,你就能非常容易地發(fā)現(xiàn)系統(tǒng)中的問題。這是很非常值得投資的。:)
日志模塊使用總結(jié):
轉(zhuǎn)載于:https://blog.51cto.com/xuanwei/1954983
總結(jié)
以上是生活随笔為你收集整理的Python自动化运维之常用模块—logging的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: greenlet 详解
- 下一篇: python写一段脚本代码自动完成输入(