slf4j優(yōu)于log4j的原因

每一個(gè)Java程序員都知道日志對(duì)于任何一個(gè)Java應(yīng)用程序,尤其是服務(wù)端程序是至關(guān)重要的,而很多程序員也已經(jīng)熟悉各種不同的日志庫(kù)如java.util.logging、Apache log4j、logback。

  • 我強(qiáng)烈建議,在java中任何新的代碼開(kāi)發(fā),都應(yīng)使用SLF4J而不是任何的日志API,包括log4J。

本文將解釋為什么使用SLF4J比log4j或者java.util.logging要優(yōu)秀?

一、抽象

SLF4J(Simple logging Facade for Java)不是一個(gè)真正的日志實(shí)現(xiàn),而是一個(gè)抽象層( abstraction layer),它允許你在后臺(tái)使用任意一個(gè)日志類庫(kù)。如果是在編寫(xiě)供內(nèi)外部都可以使用的API或者通用類庫(kù),那么你真不會(huì)希望使用你類庫(kù)的客戶端必須使用你選擇的日志類庫(kù)。

如果一個(gè)項(xiàng)目已經(jīng)使用了log4j,而你加載了一個(gè)類庫(kù),比方說(shuō) Apache Active MQ——它依賴于于另外一個(gè)日志類庫(kù)logback,那么你就需要把它也加載進(jìn)去。但如果Apache Active MQ使用了SLF4J,你可以繼續(xù)使用你的日志類庫(kù)而無(wú)語(yǔ)忍受加載和維護(hù)一個(gè)新的日志框架的痛苦。

總的來(lái)說(shuō),SLF4J使你的代碼獨(dú)立于任意一個(gè)特定的日志API,這是一個(gè)對(duì)于開(kāi)發(fā)API的開(kāi)發(fā)者很好的思想。雖然抽象日志類庫(kù)的思想已經(jīng)不是新鮮的事物而且Apache commons logging也已經(jīng)在使用這種思想了,但現(xiàn)在SLF4J正迅速成為Java世界的日志標(biāo)準(zhǔn)。

二、占位符

在代碼中表示為{}的特性。占位符是一個(gè)非常類似于在String的format()方法中的%s,它會(huì)在運(yùn)行時(shí)被某個(gè)提供的實(shí)際字符串所替換。

這不僅降低了你代碼中字符串連接次數(shù),而且還節(jié)省了新建的String對(duì)象。
因?yàn)镾tring對(duì)象是不可修改的并且它們建立在一個(gè)String池中,它們消耗堆內(nèi)存( heap memory)而且大多數(shù)時(shí)間他們是不被需要的,例如當(dāng)你的應(yīng)用程序在生產(chǎn)環(huán)境以ERROR級(jí)別運(yùn)行時(shí)候,一個(gè)String使用在DEBUG語(yǔ)句就是不被需要的。

通過(guò)使用SLF4J,你可以在運(yùn)行時(shí)延遲字符串的建立,這意味著只有需要的String對(duì)象才被建立。而如果你已經(jīng)使用log4j,那么你已經(jīng)對(duì)于在if條件中使用debug語(yǔ)句這種變通方案十分熟悉了,但SLF4J的占位符就比這個(gè)好用得多。

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,我們不需要字符串連接而且不會(huì)導(dǎo)致暫時(shí)不需要的字符串消耗。而是以一個(gè)以占位符和以參數(shù)傳遞實(shí)際值的模板格式下寫(xiě)日志信息。你可能會(huì)在想萬(wàn)一我有很多個(gè)參數(shù)怎么辦?那么你可以選擇使用變量參數(shù)版本的日志方法或者用以O(shè)bject數(shù)組傳遞。這是一個(gè)相當(dāng)?shù)姆奖愫透咝Х椒ǖ拇蛉罩痉椒?。記住,在生產(chǎn)最終日志信息的字符串之前,這個(gè)方法會(huì)檢查一個(gè)特定的日志級(jí)別是不是打開(kāi)了,這不僅降低了內(nèi)存消耗而且預(yù)先降低了CPU去處理字符串連接命令的時(shí)間。這里是使用SLF4J日志方法的代碼,來(lái)自于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());
    }
}

三、原因總結(jié)

  • 在你的開(kāi)源或內(nèi)部類庫(kù)中使用SLF4J會(huì)使得它獨(dú)立于任何一個(gè)特定的日志實(shí)現(xiàn),這意味著不需要管理多個(gè)日志配置或者多個(gè)日志類庫(kù),你的客戶端會(huì)很感激這點(diǎn)。
  • SLF4J提供了基于占位符的日志方法,這通過(guò)去除檢查isDebugEnabled(), isInfoEnabled()等等,提高了代碼可讀性。
  • 通過(guò)使用SLF4J的日志方法,你可以延遲構(gòu)建日志信息(Srting)的開(kāi)銷,直到你真正需要,這對(duì)于內(nèi)存和CPU都是高效的。
  • 作為附注,更少的暫時(shí)的字符串意味著垃圾回收器(Garbage Collector)需要做更好的工作,這意味著你的應(yīng)用程序有為更好的吞吐量和性能。
再次強(qiáng)調(diào),在java中任何新的代碼開(kāi)發(fā),都應(yīng)使用SLF4J而不是任何的日志API,包括log4J。

四、slf4j與log4j常見(jiàn)沖突

4.1. 問(wèn)題一

描述

java.lang.IllegalAccessError: tried to access field org.slf4j.impl.Static..
java.lang.IllegalAccessError: tried to access field org.slf4j.impl.StaticLoggerBinder.SINGLETON from class org.slf4j.LoggerFactory

問(wèn)題原因:jar文件版本沖突

類 org.slf4j.impl.StaticLoggerBinder在slf4j-api 中是類的公有靜態(tài)變量:

public static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder();

而在slf4j-log4j12(slf4j-nop.jar, slf4j-simple.jar, slf4j-log4j12.jar, slf4j-jdk14.jar or logback-classic.jar其中之一)中確是私有變量:

private static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder();

解決方案:

  • 修改slf的源代碼,將這個(gè)變量有私有改為公有,再打包,問(wèn)題可解決。

  • slf4j-api.jar 刪除,再導(dǎo)入同版本的slf4j-api-1.5.6.jar 和slf4j-log4j12-1.5.6.jar ,問(wèn)題可解決。

4.2 問(wèn)題二

問(wèn)題描述:

log4j:WARN No appenders could be found for logger (xxx.yyy.zzz).
log4j:WARN Please initialize the log4j system properly.

問(wèn)題解決:

在src下面新建file名為log4j.properties內(nèi)容如下:

# Configure logging for testing: optionally with log file
log4j.rootLogger=WARN, stdout
# log4j.rootLogger=WARN, stdout, logfile

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n

log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=target/spring.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n

其他情形下的問(wèn)題解決:

在Eclipse中開(kāi)發(fā)相關(guān)項(xiàng)目時(shí),在控制臺(tái)經(jīng)??吹饺缦滦畔?

log4j:WARN No appenders could be found for logger
log4j:WARN Please initialize the log4j system properly.

此處輸出信息并不是錯(cuò)誤信息而僅只是警告信息,因?yàn)閘og4j無(wú)法輸出日志,log4j是一個(gè)日志輸入軟件包??梢詫truts或Hibernate等壓縮包解壓,內(nèi)有l(wèi)og4j.properties文件,將它復(fù)制到項(xiàng)目src文件夾或?qū)og4j.properties放到 \WEB-INF\classes文件夾中即可。

4.3 問(wèn)題三

問(wèn)題描述

log4j:WARN No appenders could be found for logger (org.springframework.web.context.ContextLoader).
log4j:WARN Please initialize the log4j system properly.

問(wèn)題解決
在網(wǎng)上查了一下,多是說(shuō)把ContextLoaderListener改為SpringContextServlet,但我這樣改了沒(méi)用。后來(lái)在一個(gè)英文網(wǎng)站上看到一個(gè)遇到同樣問(wèn)題的帖子,他是這樣改的:

<context-param>
   <param-name>log4jConfigLocation</param-name>
   <param-value>/WEB-INF/config/log4j.properties</param-value>
</context-param>
<!-- 定義LOG4J監(jiān)聽(tīng)器 -->
<listener>
   <listener-class>
org.springframework.web.util.Log4jConfigListener
   </listener-class>
</listener>

這樣改了問(wèn)題就解決了,不用再修改ContextLoaderListener。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 每一個(gè)Java程序員都知道日志對(duì)于任何一個(gè)Java應(yīng)用程序,尤其是服務(wù)端程序是至關(guān)重要的,而很多程序員也已經(jīng)熟悉各...
    七弦桐語(yǔ)閱讀 17,995評(píng)論 0 12
  • 寫(xiě)Java也有一段時(shí)間了,一直都有用slf4j log4j輸出日志的習(xí)慣。但是始終都是抱著“拿來(lái)主義”的態(tài)度,復(fù)制...
    Minimumy閱讀 1,466評(píng)論 1 7
  • log4j, log4j2, slf4j, logback關(guān)系 log4j是由Apache開(kāi)發(fā)的一套元老級(jí)日志框架...
    rainybowe閱讀 1,772評(píng)論 0 4
  • 問(wèn)題 在項(xiàng)目啟動(dòng)時(shí),發(fā)現(xiàn)打印了大量的debug日志,但是src/main/resources下明明有l(wèi)og4j.x...
    Mr胡桃閱讀 22,746評(píng)論 2 11
  • 不久前讀了《一個(gè)人的好天氣》,讀的囫圇吞棗但是感慨倒還是不少的。大概我們每個(gè)人的成長(zhǎng)都是這樣的,一邊走又一邊失去,...
    不是一鳴閱讀 351評(píng)論 0 0

友情鏈接更多精彩內(nèi)容