一、介紹
Logback,一個“可靠、通用、快速而又靈活的Java日志框架”
分為以下幾個模塊:
logback-classic:log4j的一個改良版本,同時它完整提供了slf4j API是我們可以很方便地更換成其他日志系統如log4j
logback-access:訪問模塊與Servlet容器集成提供通過HTTP來訪問日志的功能
logback-core:其他兩個模塊的基礎模塊
二、取代log4j的理由
1、更快的實現:重寫了內核,在一些關鍵執行路徑上性能提升了10倍以上,而且logback不僅性能提升了,初始化內存加載也更小了
2、非常充分的測試
3、Logback-classic非常自然實現了SLF4j:Logback-classic實現了SLF4j。在使用SLF4j中,你都感覺不到logback-classic。而且因為logback-classic非常自然地實現了slf4j ,
所以切換到log4j或者其他,非常容易,只需要提供成另一個jar包就OK,根本不需要去動那些通過SLF4JAPI實現的代碼。
4、非常充分的文檔,官網有兩百多頁的文檔,官網地址:https://logback.qos.ch/index.html
5、自動重新加載配置文件,當配置文件修改了,Logback-classic能自動重新加載配置文件。掃描過程快且安全,它並不需要另外創建一個掃描線程。這個技術充分保證了應用程序能跑得很歡
在JEE環境里面。
6、Lilith是log事件的觀察者,和log4j的chainsaw類似。而lilith還能處理大數量的log數據 。
7、謹慎的模式和非常友好的恢復,在謹慎模式下,多個FileAppender實例跑在多個JVM下,能 夠安全地寫道同一個日志文件。RollingFileAppender會有些限制。Logback的FileAppender和它
的子類包括 RollingFileAppender能夠非常友好地從I/O異常中恢復。
8、配置文件可以處理不同的情況,開發人員經常需要判斷不同的Logback配置文件在不同的環境下(開發,測試,生產)。而這些配置文件僅僅只有一些很小的不同,可以通過,和來實現,這
樣一個配置文件就可以適應多個環境。
9、Filters(過濾器)有些時候,需要診斷一個問題,需要打出日志。在log4j,只有降低日志級別,不過這樣會打出大量的日志,會影響應用性能。在Logback,你可以繼續 保持那個日志級別
而除掉某種特殊情況,如alice這個用戶登錄,她的日志將打在DEBUG級別而其他用戶可以繼續打在WARN級別。要實現這個功能只需加4行XML配置。可以參考MDCFIlter 。
10、SiftingAppender(一個非常多功能的Appender):它可以用來分割日志文件根據任何一個給定的運行參數。如,SiftingAppender能夠區別日志事件跟進用戶的Session,然后每個用戶會
有一個日志文件。
11、自動壓縮已經打出來的log:RollingFileAppender在產生新文件的時候,會自動壓縮已經打出來的日志文件。壓縮是個異步過程,所以甚至對於大的日志文件,在壓縮過程中應用不會受任
何影響。
12、堆棧樹帶有包版本:Logback在打出堆棧樹日志時,會帶上包的數據。
13、自動去除舊的日志文件:通過設置TimeBasedRollingPolicy或者SizeAndTimeBasedFNATP的maxHistory屬性,你可以控制已經產生日志文件的最大數量。如果設置maxHistory 12,那那些
log文件超過12個月的都會被自動移除。
三、Logback的結構
Logback建立在三個類之上:Logger,Appender和Layouts。這三個類型的組件一起工作使開發人員能夠根據消息類型和級別記錄消息,並在運行時控制如何格式化這些消息以及在何處打印這些消息。
Logger context:指定了日志輸出的上下文
Appender(附加目的地):Logback允許將日志輸出到多個目的地,一個輸出目的地對應一個Appender。目前Appeners有,console、file、remote socket servers、to MySQL、PostgreSQL 還有其他的數據庫、JMS、remote UNIX Syslog daemons。
一個Logger可以輸出到一個或多個appender。
默認情況下,日志輸出Appender會繼承,如所有的Logger都會輸出到root指定的Appender,子包會輸出到其父包的Appender。所以想要避免重復輸出,需要指定additivity屬性為false。
如果想根據日志級別篩選appender,那就使用filter,常用的filter有只輸出某個級別的和輸出定義級別及以上級別的過濾器:
<filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>INFO</level>// 只輸出INFO級別的日志 <onMatch>ACCEPT</onMatch> // 匹配就接受 <onMismatch>DENY</onMismatch> // 不匹配就拒絕 </filter>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>INFO</level> // 接受所有INFO及以上級別的日志,WARN、ERROR </filter>
最后:實踐
pom配置:
slf4j:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.28</version>
</dependency>
<!--logback-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-access</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.11</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.11</version>
</dependency>
在工程src目錄下建立logback.xml
注:
1.logback首先會試着查找logback.groovy文件;
2.當沒有找到時,繼續試着查找logback-test.xml文件;
3.當沒有找到時,繼續試着查找logback.xml文件;
4.如果仍然沒有找到,則使用默認配置(打印到控制台)。
logback.xml配置介紹
appender:主要用於指定日志的輸出目的地,目的地可以是控制台、文件、遠程套接字服務器、MySQL等
logger:用來設置某一個包或具體的某一個類的日志打印級別,以及指定<appender>,<logger>僅有一個name屬性,一個可選的level和一個可選的addtivity屬性,可以包含零個或
多個<appender-ref>元素,標識這個appender將會添加這個logger
root:也是logger元素,是所有logger的上級,只有一個level屬性
pattern:
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS}|%thread|%-5level|%X{threadId}|%r|%C|%msg%n</pattern>
%表示日期
| 表示分隔符,可以自己指定其他分隔符
%t/thread表示線程名
%-5level:表示顯示日志的級別,從左顯示5個字符寬度
%X{threadId}:threadId是自己定義的線程號,在代碼中使用MDC.put()開始,MDC.remove()結束
MDC.put("threadId", UUID.randomUUID().toString().replaceAll("-", ""));
...
MDC.remove("threadId");
%r/relative:輸出應用程序啟動到創建日志事件所花費的毫秒數
%C/class:表示輸出日志所在類的全路徑
%msg/m/message:表示日志信息
%n:換行符
%L/line:輸出發起日志請求所在的行號
%M/method:輸出發起日志請求的方法名
一個簡單的配置:
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<!--定義日志文件的存儲地址 勿在 LogBack 的配置中使用相對路徑-->
<property name="LOG_HOME" value="/logback/LogFile"/>
<!--控制台輸出-->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%date{yyyy-MM-dd HH:mm:ss.SSS}|%thread|%-5level|%r|%X{threadId}|%C|%msg%n</pattern>
</encoder>
</appender>
<!--<logger name=""-->
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
項目中稍復雜的配置
<?xml version="1.0" encoding="UTF-8"?>
<!--默認每隔一分鍾掃描此配置文件的修改並重新加載-->
<configuration>
<!--定義日志文件的存儲地址 勿在LogBack的配置中使用相對路徑-->
<property name="LOG_HOME" value="xxx/logs"/>
<!--控制台輸出-->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%date{yyyy-MM-dd HH:mm:ss.SSS}|%t|%highlight(%-5level)|%green(%X{threadId})|%cyan(%C{0}#%M:%L)|%msg%n</pattern>
</encoder>
</appender>
<!--輸出日志到文件中-->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/info.log</file>
<!--不輸出ERROR級別的日志-->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>DENY</onMatch>
<onMismatch>ACCEPT</onMismatch>
</filter>
<!--根據日期滾動輸出日志策略-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/info.log.%d{yyyy-MM-dd}</fileNamePattern>
<!--日志保留天數-->
<maxHistory>360</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%date{yyyy-MM-dd HH:mm:ss.SSS}|%t|%-5level|%X{threadId}|%C{0}#%M:%L|%msg%n</pattern>
</encoder>
</appender>
<!--錯誤日志輸出文件-->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/error.log</file>
<!--只輸出ERROR級別的日志-->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<!--根據日期滾動輸出日志策略-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/error.log.%d{yyyy-MM-dd}</fileNamePattern>
<!--日志保留天數-->
<maxHistory>360</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%date{yyyy-MM-dd HH:mm:ss.SSS}|%t|%-5level|%X{threadId}|%C{0}#%M:%L|%msg%n</pattern>
</encoder>
</appender>
<!--異步打印日志,任務放在阻塞隊列中,如果隊列達到80%,將會丟棄TRACE,DEBUG,INFO級別的日志任務,對性能要求不是太高的話不用啟用-->
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<queueSize>1000</queueSize>
<!--設為0表示隊列達到80%,也不丟棄任務-->
<discardingThreshold>0</discardingThreshold>
<!--日志上下文關閉后,AsyncAppender繼續執行寫任務的時間,單位毫秒-->
<maxFlushTime>1000</maxFlushTime>
<!--隊列滿了直接丟棄要寫的消息-->
<neverBlock>true</neverBlock>
<!--是否包含調用方的信息,false則無法打印類名方法名行號等-->
<includeCallerData>true</includeCallerData>
<!--One and only one appender may be attached to AsyncAppender,添加多個的話后面的會被忽略-->
<appender-ref ref="FILE"/>
</appender>
<appender name="ERROR_ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<queueSize>256</queueSize>
<!--設為0表示隊列達到80%,也不丟棄任務-->
<discardingThreshold>0</discardingThreshold>
<!--日志上下文關閉后,AsyncAppender繼續執行寫任務的時間,單位毫秒-->
<maxFlushTime>1000</maxFlushTime>
<!--隊列滿了直接丟棄要寫的消息,不阻塞寫入隊列-->
<neverBlock>true</neverBlock>
<!--是否包含調用方的信息,false則無法打印類名方法名行號等-->
<includeCallerData>true</includeCallerData>
<!--One and only one appender may be attached to AsyncAppender,添加多個的話后面的會被忽略-->
<appender-ref ref="ERROR_FILE"/>
</appender>
<!--指定一些依賴包的日志輸出級別,所有的logger會繼承root,為了避免日志重復打印,需指定additivity="false",將不會繼承root的append-ref-->
<!-- <logger name="com.xxx" level="ERROR" additivity="false">
<appender-ref ref="STDOUT"/>
<!–<appender-ref ref="ERROR_FILE"/>–>
</logger>-->
<root level="INFO">
<!--<appender-ref ref="STDOUT"/>-->
<appender-ref ref="FILE"/>
<appender-ref ref="ERROR_FILE"/>
<!--使用異步打印日志-->
<!--<appender-ref ref="ASYNC"/>-->
<!--<appender-ref ref="ERROR_ASYNC"/>-->
</root>
</configuration>
