- Python自動化運維快速入門
- 鄭征
- 2884字
- 2019-12-09 14:48:20
2.5 日志記錄
日志收集與分析是運維工作中十分重要的內容,要分析日志,最好先知道日志是如何生成的,這樣才能知己知彼,分析日志才更有成效。本節將介紹如何通過Python的標準庫logging模塊定制自己多樣化的記錄日志需求。
2.5.1 日志模塊簡介
運維工作有很多情況需要查問題、解決bug,而查問題和解決bug的過程離不開查看日志,我們編寫腳本或程序時總是需要有日志輸出,Python的logging模塊就是為記錄日志使用的,而且是線程安全的,意味著使用它完全不用擔心因日志模塊的異常導致程序崩潰。
【示例2-21】首先看一下日志模塊的第一個例子。簡單將日志打印到屏幕:

輸出為:
WARNING:root:warning message ERROR:root:error message CRITICAL:root:critical message
默認情況下,Python的logging模塊將日志打印到標準輸出中,而且只顯示大于等于WARNING級別的日志,這說明默認的日志級別設置為WARNING(日志級別等級CRITICAL> ERROR > WARNING > INFO > DEBUG)。默認的日志格式:日志級別為Logger,名稱為用戶輸出消息。
各日志級別代表的含義如下。
DEBUG:調試時的信息打印。
INFO:正常的日志信息記錄。
WARNING:發生了警告信息,但程序仍能正常工作。
ERROR:發生了錯誤,部分功能已不正常。
CRITICAL:發生嚴重錯誤,程序可能已崩潰。
上面的例子是非常簡單的,還不足以顯示logging模塊的強大,因為我們使用print函數也可以實現以上功能。下面來看第二個例子。
【示例2-22】將日志信息記錄至文件(文件名:lx_log1.py)。

執行以上代碼后發現,在當前目錄多了一個文件lx_log1.log,文件內容與第一個例子的輸出是一致的。多次執行lx_log1.py發現log文件的內容變多了,說明默認的寫log文件的方式是追加。
2.5.2 logging模塊的配置與使用
我們可以通過logging模塊的配置改變log文件的寫入方式、日志級別、時間戳等信息。例如下面的配置:

可見在logging.basicConfig()函數中可通過具體參數來更改logging模塊的默認行為。
filename:用指定的文件名創建FiledHandler,這樣日志會被存儲在指定的文件中。
filemode:文件打開方式,在指定了filename時使用這個參數,默認值為a,還可指定為w。
format:指定handler使用的日志顯示格式。
datefmt:指定日期時間格式。
level:設置rootlogger的日志級別。
stream:用指定的stream創建StreamHandler。可以指定輸出到sys.stderr,sys.stdout或者文件,默認為sys.stderr。若同時列出了filename和stream兩個參數,則stream參數會被忽略。
format參數中可能用到的格式化串如下。
%(name)s Logger的名字。
%(levelno)s數字形式的日志級別。
%(levelname)s文本形式的日志級別。
%(pathname)s調用日志輸出函數的模塊的完整路徑名,可能沒有。
%(filename)s調用日志輸出函數的模塊的文件名。
%(module)s調用日志輸出函數的模塊名。
%(funcName)s調用日志輸出函數的函數名。
%(lineno)d調用日志輸出函數的語句所在的代碼行。
%(created)f當前時間,用UNIX標準表示時間的浮點數。
%(relativeCreated)d輸出日志信息時,自Logger創建以來的毫秒數。
%(asctime)s字符串形式的當前時間。默認格式是“2013-07-08 16:49:45,896”。逗號后面的是毫秒。
%(thread)d線程ID,可能沒有。
%(threadName)s線程名,可能沒有。
%(process)d進程ID,可能沒有。
%(message)s用戶輸出的消息。
【示例2-23】例如以下代碼。

運行代碼后我們會看到lx_log1.py文件的內容如下:
2018-06-07 21:09:51 lx_log1.py[line:9] DEBUG debug message 2018-06-07 21:09:51 lx_log1.py[line:10] INFO info message 2018-06-07 21:09:51 lx_log1.py[line:11] WARNING warning message 2018-06-07 21:09:51 lx_log1.py[line:12] ERROR error message 2018-06-07 21:09:51 lx_log1.py[line:13] CRITICAL critical message
這樣的配置已基本滿足我們寫一些小程序或Python腳本的日志需求。然而這還不夠體現logging模塊的強大,畢竟以上功能通過自定義一個函數也可以方便實現。下面先介紹幾個概念以及它們之間的關系圖。
logger:記錄器,應用程序代碼能直接使用的接口。
handler:處理器,將(記錄器產生的)日志記錄發送至合適的目的地。
filter:過濾器,提供了更好的粒度控制,可以決定輸出哪些日志記錄。
formatter:格式化器,指明了最終輸出中日志記錄的布局。
日志事件信息在記錄器(logger)、處理器(handler)、過濾器(filter)、格式化器(formatter)之間通過一個日志記錄實例來傳遞。通過調用記錄器實例的方法來記錄日志,每一個記錄器實例都有一個名字,名字相當于其命名空間,是一個樹狀結構。例如,一個記錄器叫scan,記錄器scan.tex、scan.html、scan.pdf的父節點。記錄器的名稱。可以任意取,但一個比較好的實踐是通過下面的方式來命名一個記錄器。
logger = logging.getLogger(__name__)
上面這條語句意味著記錄器的名字會通過搜索包的層級來獲致,根記錄器叫root logger。記錄器通過debug()、info()、warning()、error()和critical()方法記錄相應級別的日志,根記錄器也一樣。
根記錄器root logger輸出的名稱是'root'。當然,日志的輸出位置可能是不同的,logging模塊支持將日志信息輸出到終端、文件、HTTP GET/POST請求、郵件、網絡sockets、隊列或操作系統級的日志等。日志的輸出位置在處理器handler類中進行配置,如果內建的hangler類無法滿足需求,則可以自定義hander類來實現自己特殊的需求。默認情況下,日志的輸出位置為終端(標準錯誤輸出),可以通過logging模塊的basicConfig()方法指定一個具體的位置來輸出日志,如終端或文件。
logger和hander的工作流程如圖2.16所示。

圖2.16 logging模塊的工作流程
現在讓我們從整體到局部來說明logger的日志記錄過程。
第一步:獲取logger的名稱。
logger = logging.getLogger(‘logger name’) #這里的logger name是自己定義的
第二步:配置logger。
1)配置該logger的輸出級別,如logger.setLevel(loging.INFO)。
2)添加該logger的輸出位置,即logger的handler,logger.addHandler(ch)。這里ch是我們自定義的handler,如ch=logging.StreamHandler,即輸出到終端。我們可以添加多個handler,一次性將日志輸出到不同的位置。日志的輸出格式是在handler中進行配置,如ch.setFormatter(formatter),formatter也我們自定義的,如formatter =logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')。不同的hander可以配置不同的格式化器,可以實現不同的輸出位置,不同的輸出格式,完全可能靈活配置。
第三步:在應用程序中記錄日志。
logger.debug('debug message') logger.info('info message') logger.warn('warn message') logger.error('error message') logger.critical('critical message')
【示例2-24】將日志信息顯示在終端的同時也在文件中記錄(lx_log2.py)。

在以上程序中我們設置了logger的日志級別為INFO,handler ch的日志級別為DEBUG,handler fh的日志級別為WARNING,這樣做是為了解釋它們之前的優先級。
handler的日志級別以logger的日志級為基礎,logger的日志級別為INFO,低于INFO級別的(如DEBUG)均不會在handler中出現。handler中的日志級別如果高于logger,則只顯示更高級別的日志信息,如fh應該只顯示WARNING及以上的日志信息;handler中的日志級別如果低于或等于logger的日志級別,則顯示logger的日志級別及以上信息,如ch應該顯示INFO及以上的日志信息。
下面運行程序進行驗證:執行python lx_log2.py得到如下結果。
lx_log2 2018-06-12 22:18:10,378 - lx_log2 - INFO - info message 2018-06-12 22:18:10,379 - lx_log2 - WARNING - warn message 2018-06-12 22:18:10,379 - lx_log2 - ERROR - error message 2018-06-12 22:18:10,380 - lx_log2 - CRITICAL - critical message
查看lx_log2.log文件,內容如下:
2018-06-12 22:18:10,379 - lx_log2 - WARNING - warn message 2018-06-12 22:18:10,379 - lx_log2 - ERROR - error message 2018-06-12 22:18:10,380 - lx_log2 - CRITICAL - critical message
從運行結果來看,符合我們的預期。除了StreamHandler和FileHandler外,logging模塊還提供了其他更為實用的Handler子類,它們都繼承在Handler基類,如下所示。
BaseRotatingHandler:是循環日志處理器的基類,不能直接被實例化,可使用RotatingFileHandler和TimedRotatingFileHandler。
RotatingFileHandler:將日志文件記錄至磁盤文件,可以設置每個日志文件的最大占用空間。
TimedRotatingFileHandler:將日志文件記錄至磁盤文件,按固定的時間間隔來循環記錄日志。
SocketHandler:可以將日志信息發送到TCP/IP套接字。
DatagramHandler:可以將日志信息發送到UDP套接字。
SMTPHandler:可以將日志文件發送至郵箱。
SysLogHandler:系統日志處理器,可以將日志文件發送至UNIX系統日志,也可以是一個遠程機器。
NTEventLogHandler:Windows系統事件日志處理器,可以將日志文件發送到Windows系統事件日志。
MemoryHandler:MemoryHandler實例向內存中的緩沖區發送消息,只要滿足特定的條件,緩沖區就會被刷新。
HTTPHandler:使用GET或POST方法向HTTP服務器發送消息。
WatchedFileHandler:WatchedFileHandler實例監視它們登錄到的文件。如果文件發生更改,則使用文件名關閉并重新打開。這個處理器只適用于類unix系統,Windows不支持使用的底層機制。
QueueHandler:QueueHandler實例向隊列發送消息,比如在隊列或多處理模塊中實現的消息。
NullHandler:NullHandler實例不使用錯誤消息。庫開發人員使用日志記錄,但希望避免在庫用戶未配置日志記錄時顯示“日志記錄器XXX無法找到任何處理程序”消息。
【示例2-25】日志的配置信息也可以來源于配置文件(lx_log3.py)。代碼如下:

下面是配置文件的信息logging.conf。

上面幾種常用的方法已經基本滿足我們的需求,如需要更為細致的了解,可參考logging模塊的官方文檔。
- 手機安全和可信應用開發指南:TrustZone與OP-TEE技術詳解
- Apache Oozie Essentials
- 騰訊iOS測試實踐
- oreilly精品圖書:軟件開發者路線圖叢書(共8冊)
- SQL Server 2012數據庫管理與開發項目教程
- Java:High-Performance Apps with Java 9
- The Professional ScrumMaster’s Handbook
- Test-Driven Machine Learning
- MySQL入門很輕松(微課超值版)
- App Inventor創意趣味編程進階
- Regression Analysis with Python
- JSP程序設計實例教程(第2版)
- Spring技術內幕:深入解析Spring架構與設計原理(第2版)
- IoT Projects with Bluetooth Low Energy
- Python機器學習