每一個Java程序員都知道日志對于任何一個Java應用程序俱饿,尤其是服務端程序是至關重要的申屹,而很多程序員也已經熟悉各種不同的日志庫如java.util.logging、Apache log4j、logback
油够。
在這篇文章中肃弟,我們將學習為什么使用SLF4J
比log4j
或者java.util.logging
要優(yōu)秀篮幢。
SLF4J不同于其他日志類庫三椿,與其它有很大的不同。SLF4J(Simple logging Facade for Java)不是一個真正的日志實現(xiàn)伴郁,而是一個抽象層( abstraction layer)蛾绎,它允許你在后臺使用任意一個日志類庫鸦列。
當你編寫供內外部都可以使用的API或者通用類庫薯嗤,那么你真不會希望使用你類庫的客戶端必須使用你選擇的日志類庫纤泵。
如果一個項目已經使用了log4j捏题,而你加載了一個類庫,比方說 Apache Active MQ——它依賴于于另外一個日志類庫logback带射,那么你就需要把它也加載進去窟社。但如果Apache Active MQ使用了SLF4J
绪钥,你可以繼續(xù)使用你的日志類庫而加載和維護一個新的日志框架程腹。
總的來說,SLF4J使你的代碼獨立于任意一個特定的日志API缀去,這是一個對于開發(fā)API的開發(fā)者很好的思想甸祭。雖然抽象日志類庫的思想已經不是新鮮的事物而且Apache commons logging也已經在使用這種思想了池户,但現(xiàn)在SLF4J正迅速成為Java世界的日志標準凡怎。讓我們再看看幾個使用SLF4J而不是log4j统倒、logback或者java.util.logging的理由房匆。
SLF4J對比Log4J浴鸿,logback和java.util.Logging的優(yōu)勢
- 使用SLF4J寫日志語句的主要出發(fā)點是使得你的程序獨立于任意特定的日志類庫弦追,依賴于特定類可能需要不同與你已有的配置劲件,并且導致更多維護的麻煩零远。
- 占位符(place holder)的使用。
在代碼中表示為{}
的特性摔癣。占位符是一個非常類似于在String的format()
方法中的%s
供填,它會在運行時被某個提供的實際字符串所替換近她。
這不僅降低了你代碼中字符串連接次數(shù)粘捎,而且還節(jié)省了新建的String對象危彩。因為String對象是不可修改的并且它們建立在一個String池中汤徽,它們消耗堆內存( heap memory)而且大多數(shù)時間他們是不被需要的,例如當你的應用程序在生產環(huán)境以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曲楚,我們不需要字符串連接而且不會導致暫時不需要的字符串消耗褥符。而是以一個以占位符和以參數(shù)傳遞實際值的模板格式下寫日志信息喷楣。你可能會在想萬一我有很多個參數(shù)怎么辦铣焊?那么你可以選擇使用變量參數(shù)版本的日志方法或者用以Object數(shù)組傳遞。這是一個相當?shù)姆奖愫透咝Х椒ǖ拇蛉罩痉椒ㄟ椿洹S涀〉涸椋谏a最終日志信息的字符串之前懈糯,這個方法會檢查一個特定的日志級別是不是打開了赚哗,這不僅降低了內存消耗而且預先降低了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.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();
解決方案:
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中開發(fā)相關項目時,在控制臺經晨又剩看到如下信息:
log4j:WARN No appenders could be found for logger
log4j:WARN Please initialize the log4j system properly.
此處輸出信息并不是錯誤信息而僅只是警告信息,因為log4j無法輸出日志,log4j是一個日志輸入軟件包。可以將Struts或Hibernate等壓縮包解壓,內有l(wèi)og4j.properties文件荤傲,將它復制到項目src文件夾或將log4j.properties
放到 \WEB-INF\classes
文件夾中即可。
===================================
做一個SSH項目,tomcat啟動時出現(xiàn)以下問題:
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監(jiān)聽器 -->
<listener>
<listener-class>
org.springframework.web.util.Log4jConfigListener
</listener-class>
</listener>
這樣改了問題就解決了,不用再修改ContextLoaderListener
嗅辣。
總結
在你的開源或內部類庫中使用SLF4J會使得它獨立于任何一個特定的日志實現(xiàn)蛙奖,這意味著不需要管理多個日志配置或者多個日志類庫。
SLF4J提供了基于占位符的日志方法,不需要檢查isDebugEnabled(), isInfoEnabled()等等吹艇,提高了代碼可讀性鼻听。
通過使用SLF4J的日志方法灰羽,你可以延遲構建日志信息(Srting)的開銷恐似,直到你真正需要双藕,這對于內存和CPU都是高效的近范。
作為附注斥杜,更少的暫時的字符串意味著垃圾回收器(Garbage Collector)能夠做更多的工作,這意味著你的應用程序有為更好的吞吐量和性能返弹。
這些好處只是冰山一角师崎,你將在開始使用SL4J和閱讀其中代碼的時候知道更多的好處,建議使用SLF4J做日志而不是使用包括Log4J在內的其他日志API勺美。