列印 Logger 日誌時,需不需要再封裝一下工具類?
在開發過程中,列印日誌是必不可少的,因為日誌關乎於應用的問題排查、應用監控等。現在列印日誌一般都是使用 slf4j,因為使用日誌門面,有助於列印方式統一,即使後面更換日誌框架,也非常方便。在 《Java 開發手冊》中也有相關的規約。
所以在開發中,一般使用下面這種方式來列印日誌。
LOGGER.info("print: {}", "this is the log");
不過有的應用會將 LOGGER
再封裝一下,最終寫成:
LoggerUtil.info(LOGGER, "print: {}", "this is the log");
本文的主要內容是討論為什麼要封裝,有沒有必要封裝,以及怎樣封裝,如果小夥伴有更好的建議,可以提出,進行互相學習。
為什麼要封裝
很多人覺得 slf4j 本來就是日誌門面,已經封裝的很好了,為什麼要多此一舉,再額外封裝一個 LoggerUtil
呢?
其實這塊也是在開發規範中有說明的:
如果不進行封裝,則會寫成下面這種:
if (LOGGER.isInfoEnabled()) { LOGGER.info("print: {}", "this is the log"); }
所以,一般封裝是將 if 判斷這塊邏輯統一封裝為一個工具類。
可能到這裡還有小夥伴不是很理解為什麼要加 if 判斷,可以看下下面這段程式碼:
可以看出轉換邏輯這塊相對比較複雜、耗時,在這裡只是模擬的場景,實際使用可能會有其他情況,比如列印方法的出參入參、計算耗時等:
LOGGER.info("xxx 方法請求引數為:{}", JSON.toJSONString(req)); LOGGER.info("xxx 執行耗時:{}ms", System.currentTimeMillis() - startTime);
在某些場景下為了提高效能,需要關閉日誌,比如大促,秒殺等等。
說到這裡相信小夥伴已經看出問題了,因為這樣寫的話,當我關閉日誌列印時,只是關閉了磁碟輸出,但是耗時邏輯依然會繼續執行。
# 日誌級別調整到 error logging.level.com.liuzhihang=error
這也是為什麼在開發規範中建議大家手寫判斷,雖然日誌框架中幫我們進行了判斷,那只是避免了列印輸出日誌,實際上像組裝日誌,序列化例項物件等等還是會被執行的。
當然如果當前應用只有個位數的 tps 或者 tpm 那完全沒必要考慮這些,也沒必要因噎廢食,正常使用就行。
該怎樣封裝
為了避免每次都要 if 判斷的問題,會將 if 模組封裝為工具類:
上面的封裝,有效避免了每次都需要進行判斷,只需要將程式碼中的列印日誌換成 LogUtil 即可:
但是這種情況只能避免列印 既有引數
時的 if 判斷,對方法型別的沒有作用,這裡就需要使用 Supplier
:
實際使用效果:
以上僅為一種封裝方式,其他的封裝可以自行考慮,比如整個日誌框架都封裝。
其他使用
這部分封裝在 log4j-api-2.17.2.jar
中也有所體現,只不過 slf4j 裡面並沒有封裝 Supplier
支援,詳細實現可以自行閱讀原始碼。
那為什麼 slf4j 不支援,其實也是有討論的,可以看 issue #70 ,裡面進行了一系列討論。
最終結果是在 2.0 支援了 Fluent Logging API 語法。
slf4j 2.0 使用
<!-- slf4j 2.0 依賴 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>2.0.1</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>2.0.0</version> </dependency>
按照官方文件的使用案例直接使用即可:
logger.atDebug() .setMessage("Temperature set to {}. Old value was {}.") .addArgument(() -> t16()).addArgument(oldT) .log();
為什麼要這樣寫,只能說是人家的 API 設計就是如此,當然也有其他的考慮,可以看看 github issue。具體使用哪種,用不用封裝等等,這些都是根據自己的實際情況來使用。
- 執行緒池底層原理詳解與原始碼分析
- 30分鐘掌握 Webpack
- 線性迴歸大結局(嶺(Ridge)、 Lasso迴歸原理、公式推導),你想要的這裡都有
- 【前端必會】webpack loader 到底是什麼
- 中心化決議管理——雲端分析
- HashMap底層原理及jdk1.8原始碼解讀
- 詳解JS中 call 方法的實現
- 列印 Logger 日誌時,需不需要再封裝一下工具類?
- 初識設計模式 - 代理模式
- 密碼學奇妙之旅、01 CFB密文反饋模式、AES標準、Golang程式碼
- Springboot之 Mybatis 多資料來源實現
- CAS核心思想、底層實現
- 面試突擊86:SpringBoot 事務不回滾?怎麼解決?
- 基於electron vue element構建專案模板之【打包篇】
- MiniWord .NET Word模板引擎,藉由Word模板和資料簡單、快速生成檔案。
- 認識執行緒,初始併發
- 1-VSCode搭建GD32開發環境
- 初識設計模式 - 原型模式
- 執行緒安全問題的產生條件、解決方式
- 2>&1到底是什麼意思?