寧願使用SLF4J也不願使用Log4J,logback和java.util.Logging
正如我前面所說,在你的代碼中編寫日誌記錄語句使用SLF4J的主要動機是讓你的程序獨立於任何特定的日誌記錄庫,這些日誌記錄庫可能需要與你現在配置不同的配置,而且還會引入更多令人頭疼的維護問題。然而除了這個之外,SLF4J API還有一個讓你使用SLF4J而不是用長期感興趣的 Log4j 更讓人信服的功能,也就是佔位符功能,在代碼中用{}來表示。佔位符功能與 String的format()方法中 的%s非常相似,因爲它在運行時刻才提取所提供的真正的字符串。這不僅縮減了代碼中的許多字符串連接,而且減少了創建String對象所需要的資源。即便在你生產環境日誌級別比如DEBUG和INFO級別的字符串連接可能不需要的時候,仍然可以起到同樣的效果。由於 字符串是不可更改的 ,而且它們是在字符串池中創建的,這些字符串使用了 堆內存 ,當應用在生產環境中運行在ERROR級別的時候,字符串在大多數情況下就不是必須的,比如DEBUG語句裏的字符串就不是必須的。通過使用SLF4J,你可以延遲字符串的創建到運行時刻,這意味着只有在需要字符串的時候才創建它。如果你已經使用了log4j,那麼你已經熟悉把調試語句放入if()條件內的工作場景,而SLF4J佔位符功能比log4j更適合這種場景。下面是你用Log4j時的做法,當然這並不好玩而且它增加了不必要的公式化的代碼,減少了代碼的可讀性。
if (logger.isDebugEnabled()) {
logger.debug("Processing trade with id: " + id + " symbol: " + symbol);
}
而如果你使用SLF4J,你可以使用更簡潔的格式達到同樣的效果,如下:
logger.debug("Processing trade with id: {} and symbol : {} ", id, symbol);
在SLF4J中,我們不需要進行字符串拼接,不會導致使用臨時字符串帶來的消耗。相反,我們使用帶佔位符的模板消息來記錄日誌信息,並提供實際值作爲參數。也許你會想,要是有多個參數該怎麼辦,你可以使用帶參數版的日誌方法,也可以通過Object數組傳入。這確實是非常方便而且高效的記日誌的方法。記住,在爲日誌信息產生最終的字符串之前,該方法會檢查是否開啓了特定的日誌級別,這不僅降低了內存佔用,而且預先減少了執行字符串拼接所消耗的CPU時間。下面的SLF4J日誌方法的代碼,來自於slf4j-log4j12-1.6.1.jar包裏的Log4j的適配器類Log4jLoggerAdapter.
public void debug(String format, Object arg1, Object arg2) { if (logger.isDebugEnabled()) {
FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
logger.log(FQCN, Level.DEBUG, ft.getMessage(), ft.getThrowable());
}
}
同樣值得了解是,日誌也會對應用程序的性能產生壓力,大家通常宣揚的是隻在生產環境中才強制記錄日誌。
如何使用SLF4J和Log4J來做日誌
除了上面所說的好處,我認爲還有個警告需要說一下,爲了使用SLF4J你不僅需要進入SLF4J API Jar包,比如slf4j-api-1.6.1.jar,還需要引入協同工作的JAR包,具體是什麼jar包則依賴於後端你使用了什麼日誌工具庫。假如你想使用SLF4J,Simple Logging Facade for Java,還想使用Lo4J,那麼你需要把下列jar包引入到你的classpath中,具體版本要視你使用的SLF4J和log4J版本而定, 比如:
slf4j-api-1.6.1.jar - JAR for SLF4J API
log4j-1.2.16.jar - JAR for Log4J API
slf4j-log4j12-1.6.1.jar - Log4J Adapter for SLF4J
如果你正在使用Maven來管理你的項目依賴,你可以只引入SLF4J JAR,然後maven會引入它所依賴的其它JAR包。爲了使用Log4J和SLF4J,你可以在你項目的pom.xml中添加下列依賴:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.1</version>
</dependency>
順便說一下,如果你對使用帶參數版的日誌方法感興趣,那就需要引入SLF4J 1.7版本。
總結
總結這篇文章,我具有充分的理由的來選擇SLF4J而不是直接選用Log4j, commons logging, logback 或者 java.util.logging。
1)在你的開源庫或者私有庫中使用SLF4J,可以使它獨立於任何的日誌實現,這就意味着不需要管理多個庫和多個日誌文件。你的客戶端將會體會到這一點。
2)SLF4J提供了佔位日誌記錄,通過移除對isDebugEnabled(), isInfoEnabled()等等的檢查提高了代碼的可讀性。
3)通過使用日誌記錄方法,直到你使用到的時候,纔會去構造日誌信息(字符串),這就同時提高了內存和CPU的使用率。
4)做一個側面的說明,越少的臨時字符串,垃圾回收器就意味着越少的工作,這就意味着爲你的應用程序提供更好的吞吐量和性能。
這些優勢都只是冰山一角,當你開始使用SL4J並閱讀它,你會學到更多的好處。我強烈建議,在java中任何新的代碼開發,都應使用SLF4J而不是任何的日誌API,包括log4J。