天天看點

如何在Python中更優雅的記錄日志?

Crossin的程式設計教室 2月28日

以下文章來源于進擊的Coder ,作者崔慶才

如何在Python中更優雅的記錄日志?

進擊的Coder

崔慶才的個人公衆号,分享有關網絡爬蟲、Web開發、機器學習、技術心得、時事新聞、個人感悟等内容。

如何在Python中更優雅的記錄日志?

大家好,歡迎來到 Crossin的程式設計教室 !

在 Python 中,一般情況下我們可能直接用自帶的 logging 子產品來記錄日志,包括我之前的時候也是一樣。在使用時我們需要配置一些 Handler、Formatter 來進行一些處理,比如把日志輸出到不同的位置,或者設定一個不同的輸出格式,或者設定日志分塊和備份。但其實個人感覺 logging 用起來其實并不是那麼好用,其實主要還是配置較為繁瑣。

首先看看 logging 常見的解決方案吧,我一般會配置輸出到檔案、控制台和 Elasticsearch。輸出到控制台就僅僅是友善直接檢視的;輸出到檔案是友善直接存儲,保留所有曆史記錄的備份;輸出到 Elasticsearch,直接将 Elasticsearch 作為存儲和分析的中心,使用 Kibana 可以非常友善地分析和檢視運作情況。

是以在這裡我基本會對 logging 做如下的封裝寫法:

定義完了怎麼使用呢?隻需要使用定義的方法擷取一個 logger,然後 log 對應的内容即可:

運作結果如下:

我們看看這個定義的基本實作吧。首先這裡一些常量是用來定義 logging 子產品的一些基本屬性的,比如 <code>LOG_ENABLED</code> 代表是否開啟日志功能,<code>LOG_TO_ES</code> 代表是否将日志輸出到 Elasticsearch,另外還有很多其他的日志基本配置,如 <code>LOG_FORMAT</code> 配置了日志每個條目輸出的基本格式,另外還有一些連接配接的必要資訊。這些變量可以和運作時的指令行或環境變量對接起來,可以友善地實作一些開關和配置的更換。

然後定義了這麼一個 get_logger 方法,接收一個參數 name。首先該方法拿到 name 之後,會到全局的 loggers 變量裡面查找,loggers 變量是一個全局字典,如果有已經聲明過的 logger,直接将其擷取傳回即可,不用再将其二次初始化。如果 loggers 裡面沒有找到 name 對應的 logger,那就進行建立即可。建立 logger 之後,可以為其添加各種對應的 Handler,如輸出到控制台就用 StreamHandler,輸出到檔案就用 FileHandler 或 RotatingFileHandler,輸出到 Elasticsearch 就用 CMRESHandler,分别配置好對應的資訊即可。

最後呢,将建立的 logger 儲存到全局的 loggers 裡面并傳回即可,這樣如果有同名的 logger 便可以直接查找 loggers 直接傳回了。

在這裡依賴了額外的輸出到 Elasticsearch 的包,叫做 CMRESHandler,它可以支援将日志輸出到 Elasticsearch 裡面,如果要使用的話可以安裝一下:

其 GitHub 位址是:https://github.com/cmanaha/python-elasticsearch-logger,具體的使用方式可以看看它的官方說明,如配置認證資訊,配置 Index 分隔資訊等等。

好,上面就是我之前常用的 logging 配置,通過如上的配置,我就可以實作将 logging 輸出到三個位置,并可以實作對應的效果。比如輸出到 Elasticsearch 之後,我就可以非常友善地使用 Kibana 來檢視目前運作情況,ERROR Log 的比例等等,如圖所示:

也可以在它的基礎上做更進一步的統計分析。

上面的實作方式已經是一個較為可行的配置方案了。然而,我還是會感覺到有些 Handler 配起來麻煩,尤其是建立一個項目的很多時候懶得去寫一些配置。即使是不用上文的配置,用最基本的幾行 logging 配置,像如下的通用配置:

我也懶得去寫,感覺并不是一個優雅的實作方式。

有需求就有動力啊,這不,就有人實作了這麼一個庫,叫做 loguru,可以将 log 的配置和使用更加簡單和友善。

下面我們來看看它到底是怎麼用的吧。

首先,這個庫的安裝方式很簡單,就用基本的 pip 安裝即可,Python 3 版本的安裝如下:

安裝完畢之後,我們就可以在項目裡使用這個 loguru 庫了。

那麼這個庫怎麼來用呢?我們先用一個執行個體感受下:

看到了吧,不需要配置什麼東西,直接引入一個 logger,然後調用其 debug 方法即可。

在 loguru 裡面有且僅有一個主要對象,那就是 logger,loguru 裡面有且僅有一個 logger,而且它已經被提前配置了一些基礎資訊,比如比較友好的格式化、文本顔色資訊等等。

上面的代碼運作結果如下:

可以看到其預設的輸出格式是上面的内容,有時間、級别、子產品名、行号以及日志資訊,不需要手動建立 logger,直接使用即可,另外其輸出還是彩色的,看起來會更加友好。

以上的日志資訊是直接輸出到控制台的,并沒有輸出到其他的地方,如果想要輸出到其他的位置,比如存為檔案,我們隻需要使用一行代碼聲明即可。

例如将結果同時輸出到一個 runtime.log 檔案裡面,可以這麼寫:

很簡單吧,我們也不需要再聲明一個 FileHandler 了,就一行 add 語句搞定,運作之後會發現目錄下 runtime.log 裡面同樣出現了剛剛控制台輸出的 DEBUG 資訊。

上面就是一些基本的使用,但這還遠遠不夠,下面我們來詳細了解下它的一些功能子產品。

既然是日志,那麼最常見的就是輸出到檔案了。loguru 對輸出到檔案的配置有非常強大的支援,比如支援輸出到多個檔案,分級别分别輸出,過大建立新檔案,過久自動删除等等。

下面我們分别看看這些怎樣來實作,這裡基本上就是 add 方法的使用介紹。因為這個 add 方法就相當于給 logger 添加了一個 Handler,它給我們暴露了許多參數來實作 Handler 的配置,下面我們來詳細介紹下。

首先看看它的方法定義吧:

看看它的源代碼,它支援這麼多的參數,如 level、format、filter、color 等等。

另外我們還注意到它有個非常重要的參數 sink,我們看看官方文檔:https://loguru.readthedocs.io/en/stable/api/logger.html#sink,可以了解到通過 sink 我們可以傳入多種不同的資料結構,彙總如下:

•sink 可以傳入一個 file 對象,例如 <code>sys.stderr</code> 或者 <code>open('file.log', 'w')</code> 都可以。•sink 可以直接傳入一個 <code>str</code> 字元串或者 <code>pathlib.Path</code> 對象,其實就是代表檔案路徑的,如果識别到是這種類型,它會自動建立對應路徑的日志檔案并将日志輸出進去。•sink 可以是一個方法,可以自行定義輸出實作。•sink 可以是一個 logging 子產品的 Handler,比如 FileHandler、StreamHandler 等等,或者上文中我們提到的 CMRESHandler 照樣也是可以的,這樣就可以實作自定義 Handler 的配置。•sink 還可以是一個自定義的類,具體的實作規範可以參見官方文檔。

是以說,剛才我們所示範的輸出到檔案,僅僅給它傳了一個 str 字元串路徑,他就給我們建立了一個日志檔案,就是這個原理。

下面我們再了解下它的其他參數,例如 format、filter、level 等等。

其實它們的概念和格式和 logging 子產品都是基本一樣的了,例如這裡使用 format、filter、level 來規定輸出的格式:

另外添加 sink 之後我們也可以對其進行删除,相當于重新重新整理并寫入新的内容。

删除的時候根據剛剛 add 方法傳回的 id 進行删除即可,看下面的例子:

看這裡,我們首先 add 了一個 sink,然後擷取它的傳回值,指派為 trace。随後輸出了一條日志,然後将 trace 變量傳給 remove 方法,再次輸出一條日志,看看結果是怎樣的。

控制台輸出如下:

日志檔案 runtime.log 内容如下:

可以發現,在調用 remove 方法之後,确實将曆史 log 删除了。

這樣我們就可以實作日志的重新整理重新寫入操作。

用了 loguru 我們還可以非常友善地使用 rotation 配置,比如我們想一天輸出一個日志檔案,或者檔案太大了自動分隔日志檔案,我們可以直接使用 add 方法的 rotation 參數進行配置。

我們看看下面的例子:

通過這樣的配置我們就可以實作每 500MB 存儲一個檔案,每個 log 檔案過大就會新建立一個 log 檔案。我們在配置 log 名字時加上了一個 time 占位符,這樣在生成時可以自動将時間替換進去,生成一個檔案名包含時間的 log 檔案。

另外我們也可以使用 rotation 參數實作定時建立 log 檔案,例如:

這樣就可以實作每天 0 點新建立一個 log 檔案輸出了。

另外我們也可以配置 log 檔案的循環時間,比如每隔一周建立一個 log 檔案,寫法如下:

這樣我們就可以實作一周建立一個 log 檔案了。

很多情況下,一些非常久遠的 log 對我們來說并沒有什麼用處了,它白白占據了一些存儲空間,不清除掉就會非常浪費。retention 這個參數可以配置日志的最長保留時間。

比如我們想要設定日志檔案最長保留 10 天,可以這麼來配置:

這樣 log 檔案裡面就會保留最新 10 天的 log,媽媽再也不用擔心 log 沉積的問題啦。

loguru 還可以配置檔案的壓縮格式,比如使用 zip 檔案格式儲存,示例如下:

這樣可以更加節省存儲空間。

loguru 在輸出 log 的時候還提供了非常友好的字元串格式化功能,像這樣:

這樣在添加參數就非常友善了。

在很多情況下,如果遇到運作錯誤,而我們在列印輸出 log 的時候萬一不小心沒有配置好 Traceback 的輸出,很有可能我們就沒法追蹤錯誤所在了。

但用了 loguru 之後,我們用它提供的裝飾器就可以直接進行 Traceback 的記錄,類似這樣的配置即可:

我們做個測試,我們在調用時三個參數都傳入 0,直接引發除以 0 的錯誤,看看會出現什麼情況:

運作完畢之後,可以發現 log 裡面就出現了 Traceback 資訊,而且給我們輸出了當時的變量值,真的是不能再贊了!結果如下:

是以,用 loguru 可以非常友善地實作日志追蹤,debug 效率可能要高上十倍了?

另外 loguru 還有很多很多強大的功能,這裡就不再一一展開講解了,更多的内容大家可以看看 loguru 的官方文檔詳細了解一下:https://loguru.readthedocs.io/en/stable/index.html。

看完之後,是時候把自己的 logging 子產品替換成 loguru 啦!

如果文章對你有幫助,歡迎轉發/點贊/收藏~

作者:崔慶才來源:進擊的Coder