打印 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到底是什麼意思?