為什麼要使用SLF4J而不是Log4J

本文由 ImportNewJaskey 翻譯自 javarevisited。歡迎加入翻譯小組。轉載請見文末要求。

每一個Java程式設計師都知道日誌對於任何一個Java應用程式,尤其是服務端程式是至關重要的,而很多程式設計師也已經熟悉各種不同的日誌庫如java.util.logging、Apache log4j、logback。但如果你還不知道SLF4J(Simple logging facade for Java)的話,那麼是時候去在你項目中學習使用SLF4J了。 在這篇文章中,我們將學習為什麼使用SLF4J比log4j或者java.util.logging要優秀。自從上次我寫Java程式設計師的10個日誌技巧已經有一段時間了,我已經不記得我寫的關於日誌的一切了。

不管怎樣,讓我們回到這個話題,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的優勢

正如我之前說的,在你的程式碼中使用SLF4J寫日誌語句的主要出發點是使得你的程式獨立於任意特定的日誌類庫,依賴於特定類可能需要不同與你已有的配置,並且導致更多維護的麻煩。但除此之外,還要一個SLF4J API的特性使得我堅持使用SLF4J而拋棄我長期間鍾愛的Lof4j的理由,是被稱為佔位符(place holder),在程式碼中表示為「{}」的特性。佔位符是一個非常類似於在String的format()方法中的%s,因為它會在運行時被某個提供的實際字元串所替換。這不僅降低了你程式碼中字元串連接次數,而且還節省了新建的String對象。即使你可能沒需要那些對象,但這個依舊成立,取決於你的生產環境的日誌級別,例如在DEBUG或者INFO級別的字元串連接。因為String對象是不可修改的並且它們建立在一個String池中,它們消耗堆記憶體( heap memory)而且大多數時間他們是不被需要的,例如當你的應用程式在生產環境以ERROR級別運行時候,一個String使用在DEBUG語句就是不被需要的。通過使用SLF4J,你可以在運行時延遲字元串的建立,這意味著只有需要的String對象才被建立。而如果你已經使用log4j,那麼你已經對於在if條件中使用debug語句這種變通方案十分熟悉了,但SLF4J的佔位符就比這個好用得多。

這是你在Log4j中使用的方案,但肯定這一點都不有趣並且降低了程式碼可讀性因為增加了不必要的繁瑣重複程式碼(boiler-plate code):

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.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會包含它的依賴的相關包。為了和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>

還有,如果你對於使用變數參數版本(variable argument version )的日誌方法感興趣的話,那麼就導入SLF4J 1.7的版本吧。

總結

總結這次說的,我建議使用SLF4J的而不是直接使用 Log4j, commons logging, logback 或者 java.util.logging 已經足夠充分了。

  1. 在你的開源或內部類庫中使用SLF4J會使得它獨立於任何一個特定的日誌實現,這意味著不需要管理多個日誌配置或者多個日誌類庫,你的客戶端會很感激這點。
  2. SLF4J提供了基於佔位符的日誌方法,這通過去除檢查isDebugEnabled(), isInfoEnabled()等等,提高了程式碼可讀性。
  3. 通過使用SLF4J的日誌方法,你可以延遲構建日誌資訊(Srting)的開銷,直到你真正需要,這對於記憶體和CPU都是高效的。
  4. 作為附註,更少的暫時的字元串意味著垃圾回收器(Garbage Collector)需要做更好的工作,這意味著你的應用程式有為更好的吞吐量和性能。
  5. 這些好處只是冰山一角,你將在開始使用SL4J和閱讀其中程式碼的時候知道更多的好處。我強烈建議,任何一個新的Java程式設計師,都應該使用SLF4J做日誌而不是使用包括Log4J在內的其他日誌API。

更多閱讀:

  • http://javarevisited.blogspot.com/2013/08/why-use-sl4j-over-log4j-for-logging-in.html#ixzz2konULdTB

原文鏈接: javarevisited 翻譯: ImportNew.com Jaskey 譯文鏈接: http://www.importnew.com/7450.html