日志框架的概念
日志的作用是用來追蹤和記錄我們的程序運行中的信息,我們可以利用日志很快定位問題,追蹤分析。
日志級別
首先要知道日志級別是干什么用的,日志級別是當你使用不同的方式運行的時候,根據你運行的方式和你設置的日志打印級別來確定哪些信息需要輸出。
規定:日志只會打印設置的優先級及比自己高優先級的內容。
日志級別優先級排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL
- OFF:
- FATAL:
- ERROR:
- WARN:
- INFO:
- DEBUG:
- TRACE:
- ALL:
常用的日志框架
Jul: Java Util Logging
Sun公司的日志框架,原生的日志框架,優點是使用非常簡單,直接在 JDK 中就可以使用。但 JDKLog 功能比較太過於簡單,不支持占位符顯示,拓展性比較差,所以現在用的人也很少。
示例:
import java.util.logging.Logger;
/****
** JDKLog Demo
**/
public class JDKLog
{
public static void main( String[] args )
{
Logger logger = Logger.getLogger("JDKLog");
logger.info("Hello World.");
}
}
Log4j
Apache的日志框架,有多個分級(DEBUG/INFO/WARN/ERROR)記錄級別,可以很好地將不同日志級別的日志分開記錄,極大地方便了日志的查看。
- 引入依賴包
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
- log4j.properties
### 設置###
log4j.rootLogger = debug,stdout,D,E
### 輸出信息到控制抬 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
### 輸出DEBUG 級別以上的日志到=E://logs/error.log ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = E://logs/log.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
### 輸出ERROR 級別以上的日志到=E://logs/error.log ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =E://logs/error.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
Log4J2
Log4j升級版,2.x的版本在架構上進行了一些升級,配置文件也發生了一些變化。
- 引入依賴包
<!-- Log4J -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.6.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.6.2</version>
</dependency>
注:log4j和log4j2的依賴包路徑是不一樣的,這是為了區分log4j和log4j2,開發者專門設置的。
- 增加配置文件 log4j2.xml 放在 resource 目錄下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="error">
<!-- 先定義所有的appender -->
<appenders>
<!-- 這個輸出控制台的配置 -->
<Console name="Console" target="SYSTEM_OUT">
<!-- 控制台只輸出level及以上級別的信息(onMatch),其他的直接拒絕(onMismatch) -->
<ThresholdFilter level="trace" onMatch="ACCEPT" onMismatch="DENY"/>
<!-- 這個都知道是輸出日志的格式 -->
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
</Console>
<!-- 文件會打印出所有信息,這個log每次運行程序會自動清空,由append屬性決定,這個也挺有用的,適合臨時測試用 -->
<!-- append為TRUE表示消息增加到指定文件中,false表示消息覆蓋指定的文件內容,默認值是true -->
<File name="log" fileName="log/test.log" append="false">
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
</File>
<!-- 添加過濾器ThresholdFilter,可以有選擇的輸出某個級別以上的類別 onMatch="ACCEPT" onMismatch="DENY"意思是匹配就接受,否則直接拒絕 -->
<File name="ERROR" fileName="logs/error.log">
<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="%d{yyyy.MM.dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/>
</File>
<!-- 這個會打印出所有的信息,每次大小超過size,則這size大小的日志會自動存入按年份-月份建立的文件夾下面並進行壓縮,作為存檔 -->
<RollingFile name="RollingFile" fileName="logs/web.log"
filePattern="logs/$${date:yyyy-MM}/web-%d{MM-dd-yyyy}-%i.log.gz">
<PatternLayout pattern="%d{yyyy-MM-dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/>
<SizeBasedTriggeringPolicy size="2MB"/>
</RollingFile>
</appenders>
<!-- 然后定義logger,只有定義了logger並引入的appender,appender才會生效 -->
<loggers>
<!-- 建立一個默認的root的logger -->
<root level="trace">
<appender-ref ref="RollingFile"/>
<appender-ref ref="Console"/>
<appender-ref ref="ERROR" />
<appender-ref ref="log"/>
</root>
</loggers>
</configuration>
Commons Logging
日志門面,支持運行時動態加載日志組件實現。也就是說,在應用代碼中,只需要調用commons logging的接口,底層實現可以是log4j,也可以是Java Util Logging。
Slf4J
與Commons Logging對立的陣營,這是接口,實現是Logback。
Logback
Slf4J的實現。
Commons Logging與Slf4j實現機制對比
- Commons logging實現機制:Commons logging是通過動態查找機制,在程序運行時,使用自己的ClassLoader尋找和載入本地具體的實現。詳細策略可以查看commons-logging-*.jar包中的org.apache.commons.logging.impl.LogFactoryImpl.java文件。由於OSGi不同的插件使用獨立的ClassLoader,OSGI的這種機制保證了插件互相獨立, 其機制限制了commons logging在OSGi中的正常使用。
- Slf4j實現機制:Slf4j在編譯期間,靜態綁定本地的LOG庫,因此可以在OSGi中正常使用。它是通過查找類路徑下org.slf4j.impl.StaticLoggerBinder,然后綁定工作都在這類里面進。
在項目中如何選擇日志框架
-
Slf4j實現機制決定Slf4j限制較少,使用范圍更廣。由於Slf4j在編譯期間,靜態綁定本地的LOG庫使得通用性要比Commons logging要好。
-
Logback擁有更好的性能。Logback聲稱:某些關鍵操作,比如判定是否記錄一條日志語句的操作,其性能得到了顯著的提高。這個操作在Logback中需要3納秒,而在Log4J中則需要30納秒。LogBack創建記錄器(logger)的速度也更快:13毫秒,而在Log4J中需要23毫秒。更重要的是,它獲取已存在的記錄器只需94納秒,而Log4J需要2234納秒,時間減少到了1/23。跟JUL相比的性能提高也是顯著的。
-
Commons Logging開銷更高 在使Commons Logging時為了減少構建日志信息的開銷,通常的做法是:
if(log.isDebugEnabled()){
log.debug("User name: " +
user.getName() + " buy goods id :" + good.getId());
}
在Slf4j陣營,你只需這么做:
log.debug("User name:{} ,buy goods id :{}", user.getName(),good.getId());
也就是說,slf4j把構建日志的開銷放在了它確認需要顯示這條日志之后,減少內存和cup的開銷,使用占位符號,代碼也更為簡潔。
- Logback文檔免費。Logback的所有文檔是全面免費提供的,不象Log4J那樣只提供部分免費文檔而需要用戶去購買付費文檔。
如何在項目中使用Slf4J
Slf4J與其他各種日志的橋接
jar包名 | 說明 |
---|---|
slf4j-log4j12-1.7.13.jar | log4j1.2版本的橋接器,你需要將log4j.jar加入classpath。 |
slf4j-jdk14-1.7.13.jar | java.util.logging的橋接器,JDK原生日志框架。 |
slf4j-nop-1.7.13.jar | NOP橋接器,默默丟棄一切日志。 |
slf4j-simple-1.7.13.jar | 一個簡單實現的橋接器,該實現輸出所有事件到System.err. 只有INFO以及高於該級別的消息被打印,在小型應用中它也許是有用的。 |
slf4j-jcl-1.7.13.jar | Jakarta Commons Logging 的橋接器. 這個橋接器將SLF4j所有日志委派給JCL。 |
logback-classic-1.0.13.jar(requires logback-core-1.0.13.jar) | slf4j的原生實現,logback直接實現了slf4j的接口,因此使用slf4j與 logback的結合使用也意味更小的內存與計算開銷 |
具體接入方式如下:
如何橋接遺留的API
在使用不同的框架的時候,可能別人集成的框架不一樣。比如Spring Framework集成的是Commons Logging,而XSocket依賴的是Java Util Logging。現在我們需要解決不同框架中的不同日志組件都統一為Slf4J。SLF4J會根據綁定器把日志交給具體的日志實現工具。Slf4J帶有幾個橋接模塊,可以重定向log4j,JCL和Java.util.logging中的api到Slf4J。
遺留的api橋接方案
jar包名 | 作用 |
---|---|
log4j-over-slf4j-version.jar | 將log4j重定向到slf4j |
jcl-over-slf4j-version.jar | 將commons logging里的Simple Logger重定向到slf4j |
jul-to-slf4j-version.jar | 將Java Util Logging 重定向到slf4j |
橋接方式參考下圖:
使用slf4j橋接要注意事項
在使用slf4j橋接時要注意避免形成死循環,在項目依賴的jar包中不要存在以下情況:
多個日志jar包形成死循環的條件 | 產生原因 |
---|---|
log4j-over-slf4j.jar和slf4j-log4j12.jar同時存在 | 由於slf4j-log4j12.jar的存在會將所有日志調用委托給log4j。但由於同時由於log4j-over-slf4j.jar的存在,會將所有對log4j api的調用委托給相應等值的slf4j,所以log4j-over-slf4j.jar和slf4j-log4j12.jar同時存在會形成死循環 |
jul-to-slf4j.jar和slf4j-jdk14.jar同時存在 | 由於slf4j-jdk14.jar的存在會將所有日志調用委托給jdk的log。但由於同時jul-to-slf4j.jar的存在,會將所有對jul api的調用委托給相應等值的slf4j,所以jul-to-slf4j.jar和slf4j-jdk14.jar同時存在會形成死循環。 |