SLF4J使用和與Log4J對比

每一個Java程序員都知道日志對于任何一個Java應用程序,尤其是服務端程序是至關重要的,而很多程序員也已經熟悉各種不同的日志庫如java.util.logging、Apache log4j、logback

在這篇文章中,我們將學習為什么使用SLF4Jlog4j或者java.util.logging要優秀。


SLF4J不同于其他日志類庫,與其它有很大的不同。SLF4J(Simple logging Facade for Java)不是一個真正的日志實現,而是一個抽象層( abstraction layer),它允許你在后臺使用任意一個日志類庫

當你編寫供內外部都可以使用的API或者通用類庫,那么你真不會希望使用你類庫的客戶端必須使用你選擇的日志類庫。

如果一個項目已經使用了log4j,而你加載了一個類庫,比方說 Apache Active MQ——它依賴于于另外一個日志類庫logback,那么你就需要把它也加載進去。但如果Apache Active MQ使用了SLF4J,你可以繼續使用你的日志類庫而加載和維護一個新的日志框架。

總的來說,SLF4J使你的代碼獨立于任意一個特定的日志API,這是一個對于開發API的開發者很好的思想。雖然抽象日志類庫的思想已經不是新鮮的事物而且Apache commons logging也已經在使用這種思想了,但現在SLF4J正迅速成為Java世界的日志標準。讓我們再看看幾個使用SLF4J而不是log4j、logback或者java.util.logging的理由。

SLF4J對比Log4J,logback和java.util.Logging的優勢

  1. 使用SLF4J寫日志語句的主要出發點是使得你的程序獨立于任意特定的日志類庫,依賴于特定類可能需要不同與你已有的配置,并且導致更多維護的麻煩。
  2. 占位符(place holder)的使用。

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

這不僅降低了你代碼中字符串連接次數,而且還節省了新建的String對象。因為String對象是不可修改的并且它們建立在一個String池中,它們消耗堆內存( heap memory)而且大多數時間他們是不被需要的,例如當你的應用程序在生產環境以ERROR級別運行時候,一個String使用在DEBUG語句就是不被需要的。

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

使用對比

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包,這取決于你在后臺使用的日志類庫。如果你想要使用和Log4J 一起使用SLF4J ,Simple Logging Facade for Java,,你需要包含以下的Jar包在你的classpath中,取決于哪個SLF4J和你在使用的Log4J的版本。例如:

  • slf4j-api-1.5.8.jar – JAR for SLF4J API
  • log4j-1.2.16.jar – JAR for Log4J API
  • slf4j-log4j12-1.5.8.jar – Log4J Adapter for SLF4J

如果你在使用Maven去管理你的項目依賴,你只需要包含SLF4J JAR包,maven會包含它的依賴的相關包。為了和SLF4J一起中使用Log4J,你可以包含以下的依賴在你項目中的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>

使用實例

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Slf4js {
    private static Logger logger = LoggerFactory.getLogger(Slf4js.class);
    Integer t;
    Integer oldT;

    public void setTemperature(Integer temperature) {
        oldT = t;
        t = temperature;
        logger.error(" Temperature set to {}. Old temperature was {}. ", t, oldT);
        if (temperature.intValue() > 50) {
            logger.info(" Temperature has risen above 50 degrees. ");
        }
    }

    public static void main(String[] args) {
        Slf4js slf4j = new Slf4js();
        slf4j.setTemperature(1);
        slf4j.setTemperature(55);
    }
}

常見問題

1. 問題一

描述

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

問題原因:jar文件版本沖突

org.slf4j.impl.StaticLoggerBinderslf4j-api 中是類的公有靜態變量:

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();

解決方案:

1.修改slf的源代碼,將這個變量有私有改為公有,再打包,問題可解決。

2.slf4j-api.jar 刪除,再導入同版本的slf4j-api-1.5.6.jar 和slf4j-log4j12-1.5.6.jar ,問題可解決。

問題二

問題描述:

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

問題解決:

在src下面新建file名為log4j.properties內容如下:

# 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

其他情形下的問題解決:

在Eclipse中開發相關項目時,在控制臺經常看到如下信息:

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

此處輸出信息并不是錯誤信息而僅只是警告信息,因為log4j無法輸出日志,log4j是一個日志輸入軟件包。可以將Struts或Hibernate等壓縮包解壓,內有log4j.properties文件,將它復制到項目src文件夾或將log4j.properties放到 \WEB-INF\classes文件夾中即可。

===================================

做一個SSH項目,tomcat啟動時出現以下問題:

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

在網上查了一下,多是說把ContextLoaderListener改為SpringContextServlet,但我這樣改了沒用。后來在一個英文網站上看到一個遇到同樣問題的帖子,他是這樣改的:

<context-param>
   <param-name>log4jConfigLocation</param-name>
   <param-value>/WEB-INF/config/log4j.properties</param-value>
</context-param>

······

<!-- 定義LOG4J監聽器 -->
<listener>
   <listener-class>
org.springframework.web.util.Log4jConfigListener
   </listener-class>
</listener>

這樣改了問題就解決了,不用再修改ContextLoaderListener

總結

在你的開源或內部類庫中使用SLF4J會使得它獨立于任何一個特定的日志實現,這意味著不需要管理多個日志配置或者多個日志類庫。

SLF4J提供了基于占位符的日志方法,不需要檢查isDebugEnabled(), isInfoEnabled()等等,提高了代碼可讀性。

通過使用SLF4J的日志方法,你可以延遲構建日志信息(Srting)的開銷,直到你真正需要,這對于內存和CPU都是高效的。

作為附注,更少的暫時的字符串意味著垃圾回收器(Garbage Collector)能夠做更多的工作,這意味著你的應用程序有為更好的吞吐量和性能。

這些好處只是冰山一角,你將在開始使用SL4J和閱讀其中代碼的時候知道更多的好處,建議使用SLF4J做日志而不是使用包括Log4J在內的其他日志API。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,763評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,238評論 3 428
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,823評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,604評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,339評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,713評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,712評論 3 445
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,893評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,448評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,201評論 3 357
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,397評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,944評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,631評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,033評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,321評論 1 293
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,128評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,347評論 2 377

推薦閱讀更多精彩內容