我們經(jīng)晨岳看到各種代碼規(guī)約都要求我們在打印日志前先做一次日志級別判斷墨辛,例如
對于trace/debug/info級別的日志輸出英融,必須進(jìn)行日志級別的開關(guān)判斷铃彰。
這樣做的好處主要是為了性能優(yōu)化绍豁,雖然一般打印日志的方法里面都會判斷日志級別,但是在調(diào)用時可能會有一些字符串的拼接或者方法的調(diào)用牙捉,這樣就造成了一些不必要的開銷竹揍。
if (logger.isDebugEnabled()) {
? ? logger.debug("orderId is {}", getOrderId());? //避免多余的方法調(diào)用,尤其是復(fù)雜的計(jì)算
}
另外一個點(diǎn)日志打印時一般推薦的是使用占位符而不是直接的字符串拼裝鹃共,原因也是同理,因?yàn)槭褂谜嘉环姆绞娇梢詫⒆址钠囱b延遲到真正需要的時候做驶拱,避免用戶沒有進(jìn)行日志級別判斷導(dǎo)致日志內(nèi)容組裝帶來的開銷霜浴。不過使用占位符在性能上要比直接進(jìn)行字符串拼裝要稍微差一點(diǎn)點(diǎn)(因?yàn)闀幸恍└袷浇馕龅龋以诜椒ㄕ{(diào)用是會需要多余的Object[]來傳遞參數(shù)蓝纲,因此阴孟,一般日志框架會提供兩個重載的方法來替代直接使用可變參數(shù)的形式晌纫。
? ? public void trace(String format, Object arg);
public void trace(String format, Object arg1, Object arg2);? //避免創(chuàng)建Object[]來存儲參數(shù)
結(jié)合上面兩點(diǎn),無論是提前判斷級別還是使用占位符都是期望將參數(shù)的拼裝延遲到真正需要需要的時候在執(zhí)行永丝,兩者好像只需要其中一種就可以了锹漱。
如果讓我選擇,我會選擇使用占位符的方式慕嚷,畢竟在每一個打印日志的地方都加上日志級別判斷是一件非常麻煩的事情哥牍,而且代碼看起來也比較丑陋。如果要打印的日志不存在方法的調(diào)用喝检,只是單獨(dú)的字符串拼裝的話嗅辣,完全可以使用占位符,去掉煩人的級別判斷挠说。
那如果在拼裝日志時澡谭,需要復(fù)雜的計(jì)算怎么辦呢,難道只有日志級別判斷的方法么损俭?
與占位符類似蛙奖,我們可以使用一個對象將日志計(jì)算封裝起來,然后判斷日志級別后再調(diào)用對象的計(jì)算邏輯生成真正的日志字符串杆兵,不過這種方式會生成多余的一個對象雁仲。
public class LogWrapper? {
? ? ? private Logger logger;
? ? ? public void info(LogBuilder builder) {
? ? ? ? ? ? if (logger.isLoggable(Level.INFO)) {
? ? ? ? ? ? ? ? ? ? logger.info(builder.build());
? ? ? ? ? ? }
? ? ? ? }
? ? ? static interface LogBuilder {
? ? ? ? ? String build();
? ? ? }
}
為了避免每次打印日志的時候都使用new的方式來生成對象,我們可以使用lambda的方式(不過lambda并非只是簡單的語法糖拧咳,本身調(diào)用就存在一些開銷)
log.info(() -> "orderId is " + getOrderId());
這樣既可以達(dá)到延遲執(zhí)行日志計(jì)算伯顶,也可以去掉日志級別的判斷,不過如果只是簡單的字符串拼裝骆膝,還是使用占位符更經(jīng)濟(jì)祭衩,因此在wrapper中加上占位符的方法,這樣直接使用wrapper就可以了阅签。