Java日志框架那些事兒


文章首發於【博客園-陳樹義】,點擊跳轉到原文Java日志框架那些事兒

在項目開發過程中,我們可以通過 debug 查找問題。而在線上環境我們查找問題只能通過打印日志的方式查找問題。因此對於一個項目而言,日志記錄是一個非常重要的問題。因此,如何選擇一個合適的日志記錄框架也非常重要。

在Java開發中,常用的日志記錄框架有JDKLog、Log4J、LogBack、SLF4J、SLF4J。這些日志記錄框架各有各的特點,各有各的應用場景。了解這些框架的特點及應用場景,有利於我們做技術選型的時候做出正確的判斷。

JDKLog:日志小刀

JDKLog是JDK官方提供的一個記錄日志的方式,直接在JDK中就可以使用。

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.");
    }
}

JDKLog 的有點是使用非常簡單,直接在 JDK 中就可以使用。但 JDKLog 功能比較太過於簡單,不支持占位符顯示,拓展性比較差,所以現在用的人也很少。

Log4J:日志大炮

Log4J 是 Apache 的一個日志開源框架,有多個分級(DEBUG/INFO/WARN/ERROR)記錄級別,可以很好地將不同日志級別的日志分開記錄,極大地方便了日志的查看。

Log4J 有 1.X 版本和 2.X 版本,現在官方推薦使用 2.X 版本,2.X 版本在架構上進行了一些升級,配置文件也發生了一些變化。但好在官方的配置說明文檔非常清楚,通過查閱文檔能解決大部分的問題。

使用 Log4J 框架首先需要引入依賴的包:

<!-- 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>

增加配置文件 log4j2.xml 放在 resource 目錄下:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="info">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>

其中 節點的 level 屬性表示輸出的最低級別。

最后編寫一個測試類:

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
 
/****
 ** Log4J Demo
 **/
public class Log4jLog {
    public static void main(String args[]) {
        Logger logger = LogManager.getLogger(Log4jLog.class);
        logger.debug("Debug Level");
        logger.info("Info Level");
        logger.warn("Warn Level");
        logger.error("Error Level");
    }
}

運行測試類輸出結果:

10:16:08.279 [main] INFO  com.chanshuyi.Log4jLog - Info Level
10:16:08.280 [main] WARN  com.chanshuyi.Log4jLog - Warn Level
10:16:08.280 [main] ERROR com.chanshuyi.Log4jLog - Error Level

如果沒有配置 log4j2.xml 配置文件,那么LOG4J將自動啟用類似於下面的的配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="error">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>

使用默認配置文件的輸出結果:

ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console.
11:40:07.377 [main] ERROR com.chanshuyi.Log4jLog - Error Level

從上面的使用步驟可以看出 Log4J 的使用稍微復雜一些,但是條理還是很清晰的。而且因為 Log4J 有多個分級(DEBUG/INFO/WARN/ERROR)記錄級別,所以可以很好地記錄不同業務問題。因為這些優點,所以在幾年前幾乎所有人都使用 Log4J 作為日志記錄框架,群眾基礎可謂非常深厚。

但 Log4J 本身也存在一些缺點,比如不支持使用占位符,不利於代碼閱讀等缺點。但是相比起 JDKLog,Log4J 可以說是非常好的日志記錄框架了。

LogBack:日志火箭

LogBack 其實可以說是 Log4J 的進化版,因為它們兩個都是同一個人(Ceki Gülcü)設計的開源日志組件。LogBack 除了具備 Log4j 的所有優點之外,還解決了 Log4J 不能使用占位符的問題。

使用 LogBack 需要首先引入依賴:

<!-- LogBack -->
<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>1.1.7</version>
</dependency>

配置 logback.xml 配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern>
        </layout>
    </appender>
    <logger name="com.chanshuyi" level="TRACE"/>
 
    <root level="debug">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

LogBack 的日志級別區分可以細分到類或者包,這樣就可以使日志記錄變得更加靈活。之后在類文件中引入Logger類,並進行日志記錄:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
/****
 ** LogBack Demo
 **/
public class LogBack {
    static final Logger logger = LoggerFactory.getLogger(LogBack.class);
    public static void main(String[] args) {
        logger.trace("Trace Level.");
        logger.debug("Debug Level.");
        logger.info("Info Level.");
        logger.warn("Warn Level.");
        logger.error("Error Level.");
    }
}

輸出結果:

14:34:45.747 [main] TRACE com.chanshuyi.LogBack - Trace Level.
14:34:45.749 [main] DEBUG com.chanshuyi.LogBack - Debug Level.
14:34:45.749 [main] INFO  com.chanshuyi.LogBack - Info Level.
14:34:45.749 [main] WARN  com.chanshuyi.LogBack - Warn Level.
14:34:45.749 [main] ERROR com.chanshuyi.LogBack - Error Level.

LogBack 解決了 Log4J 不能使用占位符的問題,這使得閱讀日志代碼非常方便。除此之外,LogBack 比 Log4J 有更快的運行速度,更好的內部實現。並且 LogBack 內部集成了 SLF4J 可以更原生地實現一些日志記錄的實現。

SLF4J:適配器

上面說了 JDKLog、Log4J、LogBack 這幾個常用的日志記錄框架,它們都有各自的優缺點,適合在不同的場景下使用。可能簡單的項目直接用 JDKLog 就可以了,而復雜的項目需要用上 Log4J。

很多時候我們做項目都是從簡單到復雜,也就是我們很可能一開始使用的是 JDKLog,之后業務復雜了需要使用 Log4J,這時候我們如何將原來寫好的日志用新的日志框架輸出呢?

一個最死板的方法就是一行行代碼修改,把之前用 JDKLog 的日志代碼全部修改成 Log4J 的日志接口。但是這種方式不僅效率低下,而且做的工作都是重復性的工作,這怎么能忍呢。

正式因為在實際的項目應用中,有時候可能會從一個日志框架切換到另外一個日志框架的需求,這時候往往需要在代碼上進行很大的改動。為了避免切換日志組件時要改動代碼,這時候一個叫做 SLF4J(Simple Logging Facade for Java,即Java簡單日志記錄接口集)的東西出現了。

SLF4J(Simple Logging Facade for Java,即Java簡單日志記錄接口集)是一個日志的接口規范,它對用戶提供了統一的日志接口,屏蔽了不同日志組件的差異。這樣我們在編寫代碼的時候只需要看 SLF4J 這個接口文檔即可,不需要去理會不同日之框架的區別。而當我們需要更換日志組件的時候,我們只需要更換一個具體的日志組件Jar包就可以了。

而整合 SLF4J 和日志框架使用也是一件很簡單的事情。

SLF4J+JDKLog

SLF4J + JDKLog 需要在 Maven 中導入以下依賴包:

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>1.7.21</version>
</dependency>
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-jdk14</artifactId>
  <version>1.7.21</version>
</dependency>

編寫測試類:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
/****
 ** SLF4J + JDKLog
 **/
public class Slf4jJDKLog {
    final static Logger logger = LoggerFactory.getLogger(Slf4jJDKLog.class);
    public static void main(String[] args) {
        logger.trace("Trace Level.");
        logger.info("Info Level.");
        logger.warn("Warn Level.");
        logger.error("Error Level.");
    }
}

輸出結果:

七月 15, 2016 3:30:02 下午 com.chanshuyi.slf4j.Slf4jJDKLog main
信息: Info Level.
七月 15, 2016 3:30:02 下午 com.chanshuyi.slf4j.Slf4jJDKLog main
警告: Warn Level.
七月 15, 2016 3:30:02 下午 com.chanshuyi.slf4j.Slf4jJDKLog main
嚴重: Error Level.

SLF4J+LOG4J

需要依賴的 Jar 包:slf4j-api.jar、slf4j-412.jar、log4j.jar,導入Maven依賴:

<!-- 2.SLF4J + Log4J -->
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>1.7.21</version>
</dependency>
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
  <version>1.7.21</version>
</dependency>
<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.17</version>
</dependency>

配置 log4j.xml 文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
 
<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/' >
 
    <appender name="myConsole" class="org.apache.log4j.ConsoleAppender">
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern"
                   value="[%d{dd HH:mm:ss,SSS\} %-5p] [%t] %c{2\} - %m%n" />
        </layout>
        <!--過濾器設置輸出的級別-->
        <filter class="org.apache.log4j.varia.LevelRangeFilter">
            <param name="levelMin" value="debug" />
            <param name="levelMax" value="error" />
            <param name="AcceptOnMatch" value="true" />
        </filter>
    </appender>
 
    <!-- 根logger的設置-->
    <root>
        <priority value ="debug"/>
        <appender-ref ref="myConsole"/>
    </root>
</log4j:configuration>

我們還是用上面的代碼,無需做改變,運行結果為:

[15 16:04:06,371 DEBUG] [main] slf4j.SLF4JLog - Debug Level.
[15 16:04:06,371 INFO ] [main] slf4j.SLF4JLog - Info Level.
[15 16:04:06,371 WARN ] [main] slf4j.SLF4JLog - Warn Level.
[15 16:04:06,371 ERROR] [main] slf4j.SLF4JLog - Error Level.

SLF4J+LogBack

導入依賴:

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>1.7.21</version>
</dependency>
<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>1.1.7</version>
</dependency>
<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-core</artifactId>
  <version>1.1.7</version>
</dependency>

配置 logback.xml 文件:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern>
        </layout>
    </appender>
    <logger name="com.chanshuyi" level="TRACE"/>
 
    <root level="warn">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

我們還是用上面的代碼,無需做改變,運行結果為:

16:08:01.040 [main] TRACE com.chanshuyi.slf4j.SLF4JLog - Trace Level.
16:08:01.042 [main] DEBUG com.chanshuyi.slf4j.SLF4JLog - Debug Level.
16:08:01.043 [main] INFO  com.chanshuyi.slf4j.SLF4JLog - Info Level.
16:08:01.043 [main] WARN  com.chanshuyi.slf4j.SLF4JLog - Warn Level.
16:08:01.043 [main] ERROR com.chanshuyi.slf4j.SLF4JLog - Error Level.

LogBack日志框架

經過上面的介紹,相信大家對 Java 常用的日志框架都有了一定認識。

那么在實際使用中到底選擇哪種日志框架合適呢?

按筆者理解,現在最流的日志框架解決方案莫過於SLF4J + LogBack。原因有下面幾點:

  • LogBack 自身實現了 SLF4J 的日志接口,不需要 SLF4J 去做進一步的適配。
  • LogBack 自身是在 Log4J 的基礎上優化而成的,其運行速度和效率都比 LOG4J 高。
  • SLF4J + LogBack 支持占位符,方便日志代碼的閱讀,而 LOG4J 則不支持。

從上面幾點來看,SLF4J + LogBack是一個較好的選擇。

LogBack 被分為3個組件:logback-core、logback-classic 和 logback-access。

  • logback-core 提供了 LogBack 的核心功能,是另外兩個組件的基礎。
  • logback-classic 則實現了 SLF4J 的API,所以當想配合 SLF4J 使用時,需要將 logback-classic 引入依賴中。
  • logback-access 是為了集成Servlet環境而准備的,可提供HTTP-access的日志接口。

LogBack的日志記錄數據流是從 Class(Package)到 Logger,再從Logger到Appender,最后從Appender到具體的輸出終端。

LogBack配置文件可以分為幾個節點,其中 Configuration 是根節點,Appender、Logger、Root是Configuration的子節點。

appender節點

的子節點,是負責寫日志的組件。appender有兩個必要屬性name、class 。name指定appender的名稱,class指定appender的全限定名
class,主要包括:

  • ch.qos.logback.core.ConsoleAppender 控制台輸出
  • ch.qos.logback.core.FileAppender 文件輸出
  • ch.qos.logback.core.RollingFileAppender 文件滾動輸出
<?xml version="1.0" encoding="utf-8"?> 
<configuration debug="true" scan="true" scanPeriod="2">
    <!-- conf consoel out -->
    <appender name ="console_out" class="ch.qos.logback.core.ConsoleAppender">
    </appender>

    <!-- conf file out -->
    <appender name="file_out" class="ch.qos.logback.core.FileAppender">
    </appender>
    
    <!-- conf file out -->
    <appender name="file_out" class="ch.qos.logback.core.RollingFileAppender">
    </appender>

    <root></root>
    <logger></logger>
</configuration>

ConsoleAppender

把日志添加到控制台,有如下節點:

  • <encoder> : 對日志進行格式化。
  • <target> : 字符串System.out 或者 System.err, 默認 System.out;
<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <!-- conf consoel out -->
  <appender name ="console_out" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%date [%thread] %-5level %logger - %message%newline</pattern>
        </encoder>
  </appender>

  <root level="INFO">             
    <appender-ref ref="console_out" />   
  </root>     
</configuration>

FileAppender

把日志添加到文件,有如下節點:

  • <file>:被寫入的文件名,可以是相對目錄 , 也可以是絕對目錄 , 如果目錄不存在則會自動創建。
  • <append>:如果是true , 日志被追加到文件結尾 , 如果是false,清空現存文件 , 默認是true。
  • <encoder>:對日志進行格式化 [具體的轉換符說明請參見官網.]
<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <appender name="file_out" class="ch.qos.logback.core.FileAppender">
        <file>logs/debug.log</file>
        <encoder>
            <pattern>%date [%thread] %-5level %logger - %message%newline</pattern>
        </encoder>
    </appender>
</configuration>

rollingFileAppender

滾動紀錄文件,先將日志記錄到指定文件,當符合某種條件時,將日志記錄到其他文件,有如下節點:

  • <file>:被寫入的文件名,可以是相對目錄,也可以解決目錄,如果目錄不存在則自動創建。
  • <append>:如果是true,日志被追加到文件結尾,如果是false,清空現存文件,默認是true。
  • <encoder>:對日志進行格式化。
  • <rollingPolicy>:當發生滾動時,決定 RollingFileAppender 的行為,涉及文件移動和重命名。

rollingPolicy

  • TimeBaseRollingPolicy :最常用的滾動策略,根據時間來制定滾動策略,即負責滾動也負責觸發滾動。有如下節點;

    • <fileNamePattern>:必要節點,包含文件及“%d” 轉換符,“%d”可以包含一個java.text.SimpleDateFormat 制定的時間格式,如:%d{yyyy-MM},如果直接使用%d ,默認格式是 yyyy-MM-dd。
    • <maxHistory>:可選節點,控制保留的歸檔文件的最大數量,超出數量就刪除舊文件,假設設置每個月滾動,且 是 6,則只保存最近6個月的文件,刪除之前的舊文件,注意:刪除舊文件是哪些為了歸檔而創建的目錄也會被刪除。
    • <filenamePattern>:必須包含“%i” 例如:設置最小值,和最大值分別為1和2,命名模式為 log%i.log,會產生歸檔文件log1.log和log2.log,還可以指定文件壓縮選項,例如:log%i.log.gz 或者 log%i.log.zip
  • triggeringPolicy:告知RollingFileAppender,激活RollingFileAppender滾動。

<!-- 03:conf errorAppender out -->
<appender name="errorAppender" class="ch.qos.logback.core.RollingFileAppender">
    <file>logs/error.log</file>
    <!-- 設置滾動策略 -->
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">  
        <!--設置日志命名模式--> 
        <fileNamePattern>errorFile.%d{yyyy-MM-dd}.log</fileNamePattern>
        <!--最多保留30天log-->
        <maxHistory>30</maxHistory>
    </rollingPolicy>
    <!-- 超過150MB時,觸發滾動策略 -->
    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
        <maxFileSize>150</maxFileSize>
    </triggeringPolicy>
    <encoder>
        <pattern>%d [%p] %-5level %logger - %msg%newline</pattern>
    </encoder>
</appender>

logger節點

logger是 的子節點,來設置某一個包或者具體的某一個類的日志打印級別,以及指定 。logger僅有一個name屬性,兩個可選屬性 level/addtivity。

  • name:用來指定受此loger約束的某一個包或者具體的某一個類。
  • level:用來設置打印級別,大小寫無關。可選值有TRACE、DEBUG、INFO、WARN、ERROR、ALL和OFF。還有一個特俗值INHERITED 或者 同義詞NULL,代表強制執行上級的級別。如果未設置此屬性,那么當前logger將會繼承上級的級別。
  • addtivity:是否向上級logger傳遞打印信息,默認為true;

可以包含零個或多個 元素,表示這個appender將會添加到logger。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <!-- conf consoel out -->
    <appender name ="console_out" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 過濾掉非INFO級別 -->
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!--  conf infoAppender out -->
    <appender name="infoAppender" class="ch.qos.logback.core.RollingFileAppender">
        <file>logs/info.log</file>
        <!-- 設置滾動策略 -->
        <rollingPoliy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">  
            <!--設置日志命名模式--> 
            <fileNamePattern>infoFile.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!--最多保留30天log-->
            <maxHistory>30</maxHistory>
        </rollingPoliy>
        <!-- 超過150MB時,觸發滾動策略 -->
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <maxFileSize>150</maxFileSize>
        </triggeringPolicy>
        <encoder>
            <pattern>%d [%p] %-5level %logger - %msg%newline</pattern>
        </encoder>
    </appender>
 
    <!-- 添加兩個appender節點 -->
    <logger name="logback.olf.log" level="info">
        <appender-ref ref = "console_out"/>
        <appender-ref ref = "infoAppender"/>
    </logger>
</configuration>

root節點

元素配置根logger。該元素有一個level屬性,沒有name屬性,因為已經被命名 為root。Level屬性的值大小寫無關,其值為下面其中一個字符串:TRACE、DEBUG、INFO、 WARN、ERROR、ALL 和 OFF。如果 root 元素沒 有引用任何 appender,就會失去所有 appender。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <!-- conf consoel out -->
    <appender name ="console_out" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 過濾掉非INFO級別 -->
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- 01:conf infoAppender out -->
    <appender name="infoAppender" class="ch.qos.logback.core.RollingFileAppender">
        
        <file>logs/info.log</file>
        <!-- 設置滾動策略 -->
        <rollingPoliy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">  
            <!--設置日志命名模式--> 
            <fileNamePattern>infoFile.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!--最多保留30天log-->
            <maxHistory>30</maxHistory>
        </rollingPoliy>
        <!-- 超過150MB時,觸發滾動策略 -->
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <maxFileSize>150</maxFileSize>
        </triggeringPolicy>
        <encoder>
            <pattern>%d [%p] %-5level %logger - %msg%newline</pattern>
        </encoder>
    </appender>

    <!-- 02:conf debugAppender out -->
    <appender name="debugAppender" class="ch.qos.logback.core.RollingFileAppender">
        <file>logs/debug.log</file>
        <!-- 設置滾動策略 -->
        <rollingPoliy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">  
            <!--設置日志命名模式--> 
            <fileNamePattern>debugFile.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!--最多保留30天log-->
            <maxHistory>30</maxHistory>
        </rollingPoliy>
        <!-- 超過150MB時,觸發滾動策略 -->
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <maxFileSize>150</maxFileSize>
        </triggeringPolicy>
        <encoder>
            <pattern>%d [%p] %-5level %logger - %msg%newline</pattern>
        </encoder>
    </appender>

    <!-- 03:conf errorAppender out -->
    <appender name="errorAppender" class="ch.qos.logback.core.RollingFileAppender">
        <file>logs/error.log</file>
        <!-- 設置滾動策略 -->
        <rollingPoliy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">  
            <!--設置日志命名模式--> 
            <fileNamePattern>errorFile.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!--最多保留30天log-->
            <maxHistory>30</maxHistory>
        </rollingPoliy>
        <!-- 超過150MB時,觸發滾動策略 -->
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <maxFileSize>150</maxFileSize>
        </triggeringPolicy>
        <encoder>
            <pattern>%d [%p] %-5level %logger - %msg%newline</pattern>
        </encoder>
    </appender>
 
    <root level="ALL">
        <appender-ref ref="infoAppender"/>
        <appender-ref ref="debugAppender"/>
        <appender-ref ref="errorAppender"/>
    </root>
</configuration>

filter過濾節點

級別過濾器(LevelFilter)

LevelFilter 根據記錄級別對記錄事件進行過濾。如果事件的級別等於配置的級別,過濾 器會根據 onMatch 和 onMismatch 屬性接受或拒絕事件。下面是個配置文件例子:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <!-- conf consoel out -->
    <appender name ="console_out" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 過濾掉非INFO級別 -->
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        
        <encoder>
            <pattern>%-4relative [%thread] %-5level %logger{30} - %msg%n</pattern>
        </encoder>
    </appender>
    <root level="DEBUG">
        <appender-ref ref="console_out" />
    </root>
</configuration>

臨界值過濾器(ThresholdFilter)

ThresholdFilter過濾掉低於指定臨界值的事件。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <!-- conf consoel out -->
    <appender name ="console_out" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">  
        <!-- 過濾掉TRACE和DEBUG級別的日志 -->
            <level>INFO</level> 
        </filter>
        
        <encoder>
            <pattern>%-4relative [%thread] %-5level %logger{30} - %msg%n</pattern>
        </encoder>
    </appender>
    <root level="DEBUG">
        <appender-ref ref="console_out" />
    </root>
</configuration>

求值過濾器(EvaluatorFilter)

評估是否符合指定的條件

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <!-- conf consoel out -->
    <appender name ="console_out" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.EvaluatorFilter">  
             <evaluator>
             <!--過濾掉所有日志中不包含hello字符的日志-->
                <expression>
                    message.contains("hello")
                </expression>
                <onMatch>NEUTRAL</onMatch>
                <onMismatch>DENY</onMismatch>
             </evaluator>
        </filter>
        
        <encoder>
            <pattern>%-4relative [%thread] %-5level %logger{30} - %msg%n</pattern>
        </encoder>
    </appender>
    <root level="DEBUG">
        <appender-ref ref="console_out" />
    </root>
</configuration>

匹配器(Matchers)

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <!-- conf consoel out -->
    <appender name ="console_out" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.EvaluatorFilter">  
             <evaluator> 
                <matcher>
                    <Name>odd</Name>
                    <!-- 過濾掉序號為奇數的語句-->
                    <regex>statement [13579]</regex>
                </matcher>
                <expression>odd.matches(formattedMessage)</expression>
                <onMatch>NEUTRAL</onMatch>
                <onMismatch>DENY</onMismatch>
             </evaluator>
        </filter>
        
        <encoder>
            <pattern>%-4relative [%thread] %-5level %logger{30} - %msg%n</pattern>
        </encoder>
    </appender>
    <root level="DEBUG">
        <appender-ref ref="console_out" />
    </root>
</configuration>

下面是一個我常用的logback.xml配置文件,供大家參考:

<?xml version="1.0" encoding="UTF-8"?>

<configuration debug="true" scan="true" scanPeriod="30 seconds"> 

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> 
    <!-- encoders are  by default assigned the type
         ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss} [%level] - %m%n</pattern>
        
        <!-- 常用的Pattern變量,大家可打開該pattern進行輸出觀察 -->
        <!-- 
          <pattern>
              %d{yyyy-MM-dd HH:mm:ss} [%level] - %msg%n
              Logger: %logger
              Class: %class
              File: %file
              Caller: %caller
              Line: %line
              Message: %m
              Method: %M
              Relative: %relative
              Thread: %thread
              Exception: %ex
              xException: %xEx
              nopException: %nopex
              rException: %rEx
              Marker: %marker
              %n
              
          </pattern>
           -->
    </encoder>
  </appender>
  
  <!-- 按日期區分的滾動日志 -->
  <appender name="ERROR-OUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
      <file>logs/error.log</file>
      
    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss} [%class:%line] - %m%n</pattern>
    </encoder>
      
      <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">
      <!-- daily rollover -->
      <fileNamePattern>error.%d{yyyy-MM-dd}.log.zip</fileNamePattern>

      <!-- keep 30 days' worth of history -->
      <maxHistory>30</maxHistory>
    </rollingPolicy>
  </appender>
  
  <!-- 按文件大小區分的滾動日志 -->
  <appender name="INFO-OUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
      <file>logs/info.log</file>
      
    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss} [%class:%line] - %m%n</pattern>
    </encoder>
    
      <filter class="ch.qos.logback.classic.filter.LevelFilter">
      <level>INFO</level>
      <onMatch>ACCEPT</onMatch>
      <onMismatch>DENY</onMismatch>
    </filter>
      
      <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
      <fileNamePattern>info.%i.log</fileNamePattern>
      <minIndex>1</minIndex>
      <maxIndex>3</maxIndex>
    </rollingPolicy>
    
    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
      <maxFileSize>5MB</maxFileSize>
    </triggeringPolicy>
    
  </appender>
  
  
  <!-- 按日期和大小區分的滾動日志 -->
  <appender name="DEBUG-OUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
      <file>logs/debug.log</file>

    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss} [%class:%line] - %m%n</pattern>
    </encoder>
      
      <filter class="ch.qos.logback.classic.filter.LevelFilter">
      <level>DEBUG</level>
      <onMatch>ACCEPT</onMatch>
      <onMismatch>DENY</onMismatch>
    </filter>
      
      <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <!-- rollover daily -->
      <fileNamePattern>debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
      
      <timeBasedFileNamingAndTriggeringPolicy
            class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
        <!-- or whenever the file size reaches 100MB -->
        <maxFileSize>100MB</maxFileSize>
      </timeBasedFileNamingAndTriggeringPolicy>
      
    </rollingPolicy>
    
  </appender>
  
  
   <!-- 級別閥值過濾 -->
  <appender name="SUM-OUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
      <file>logs/sum.log</file>

    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss} [%class:%line] - %m%n</pattern>
    </encoder>
      
    <!-- deny all events with a level below INFO, that is TRACE and DEBUG -->
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
      <level>INFO</level>
    </filter>

      
      <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <!-- rollover daily -->
      <fileNamePattern>debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
      
      <timeBasedFileNamingAndTriggeringPolicy
            class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
        <!-- or whenever the file size reaches 100MB -->
        <maxFileSize>100MB</maxFileSize>
      </timeBasedFileNamingAndTriggeringPolicy>
      
    </rollingPolicy>
    
  </appender>
  
  
  <root level="debug">
    <appender-ref ref="STDOUT" />
    <appender-ref ref="ERROR-OUT" />
    <appender-ref ref="INFO-OUT" />
    <appender-ref ref="DEBUG-OUT" />
    <appender-ref ref="SUM-OUT" />
  </root>
</configuration>

如何進行日志系統轉換?

在實際的日志轉換過程中,SLF4J其實是充當了一個中介的角色。例如當我們一個項目原來是使用LOG4J進行日志記錄,但是我們要換成LogBack進行日志記錄。

此時我們需要先將LOG4J轉換成SLF4J日志系統,再從SLF4J日志系統轉成LogBack日志系統。

從日志框架轉向SLF4J

  • jul-to-slf4j:jdk-logging到slf4j的橋梁
  • log4j-over-slf4j:log4j1到slf4j的橋梁
  • jcl-over-slf4j:commons-logging到slf4j的橋梁

從SLF4J轉向具體的日志框架

  • slf4j-jdk14:slf4j到jdk-logging的橋梁
  • slf4j-log4j12:slf4j到log4j1的橋梁
  • log4j-slf4j-impl:slf4j到log4j2的橋梁
  • logback-classic:slf4j到logback的橋梁
  • slf4j-jcl:slf4j到commons-logging的橋梁

例如我們一開始使用的是 Log4J 日志框架,現在我們希望轉成 LogBack 框架,那么我們首先需要加入 log4j-over-slf4j.jar 將 Log4J 轉成 SLF4J,之后再加入 logback-classic.jar 將 SLF4J 轉成 LogBack。

日志技術框架一覽

  • JUL:JDK中的日志記錄工具,也常稱為JDKLog、jdk-logging。
  • LOG4J1:一個具體的日志實現框架。
  • LOG4J2:一個具體的日志實現框架,是LOG4J1的下一個版本。
  • LOGBACK:一個具體的日志實現框架,但其性能更好。
  • JCL:一個日志門面,提供統一的日志記錄接口,也常稱為commons-logging。
  • SLF4J:一個日志門面,與JCL一樣提供統一的日志記錄接口,可以方便地切換看具體的實現框架。

JUL、LOG4J1、LOG4J2、LOGBACK是日志實現框架,而JCL、SLF4J是日志實現門面

文章首發於【博客園-陳樹義】,點擊跳轉到原文Java日志框架那些事兒


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM