logback的使用與配置教程

logback指南(A Guide To Logback)

1. 總體介紹(Overview)

logback是java社區使用最廣泛的日誌框架之一。它是Log4j的繼任者。相對於Log4j,logback實現了更好的性能、提供了更多的配置選項以及更靈活的就日誌文件歸檔。
本文將介紹logback的架構,幫助你在應用中更好的使用它。

2. Logback架構(Logback Architecture)

logback主要由三部分組成:Logger、Appender、以及Layout。

  • logger是打印日誌消息的內容。該類主要是應用程序創建用於打印日誌的對象。即創建該對象,使用該對象用於打印我們需要的日誌。
  • appenders 設置了日誌消息最終的目的地,即具體的日誌內容輸出的地方。一個日誌對象(logger)可以有多個appender。我們通常想到的就是將appender設置爲文本文件(即日誌內容存放到文件中),但是遠不止這些(比如設置爲直接輸出到控制檯)。
  • layout準備輸出的消息(譯者注:也就是格式化輸出的日誌消息內容,比如時間,類,具體的消息等)。logback支持創建格式化消息的傳統類,也支持根據已經存在的進行模糊配置。

3. 設置

3.1 maven依賴

logback使用java簡單日誌門面模式(Simple Logging Facade for Java)(SLF4J)作爲其實現接口。在我們使用logback之前,需要在pom.xml中添加需要的依賴。

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>1.2.3</version>
        </dependency>
		<!--注,原文中使用的是1.8.0-beta2版本,目前看沒有1.8.0的正式版,而使用該beta版無法識別logback.xml配置文件,無法使用,所以使用的是最新的1.7.x版本-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>

想使用更新的版本可以去maven中央倉庫中去查詢是否有最新的版本。

4. 基本例子和配置(Basic Example and Configuration)

讓我們在應用中快速搭建使用一個例子。
首先,需要創建一個取名爲logback.xml的配置文件,直接放到classpath路徑下。

<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%msg%n</pattern>
        </encoder>
    </appender>

    <root level="debug">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

然後創建一個main類:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author hexin
 * @version 1.0 2018/11/13
 */
public class Test {
    private final static Logger logger = LoggerFactory.getLogger(Test.class);

    public static void main(String[] args) {
        logger.info("Example log from {}", Test.class.getSimpleName());
    }
}

//output:
16:48:39.319 [main] INFO  com.hexin.learning.logback.Test -Example log from Test

以上就運行成功了,這也是爲什麼logback這麼受歡迎的原因–我們可以很快搭建上手。
從以上配置文件以及代碼可以推斷出其工作方式:

  1. 配置了一個取名爲STDOUT引用ConsoleAppender類的appender
  2. 指定了日誌消息輸出格式的模板
  3. 在代碼中創建一個Logger對象,並且通過info()方法輸出消息。

5. 日誌對象(Logger Context)

5.1 創建一個日誌對象

要使用logback打印日誌,首先需要通過SLF4J或者Logback創建一個日誌對象

 private final static Logger logger = LoggerFactory.getLogger(Test.class);

然後使用:

logger.info("Example log from {}", Test.class.getSimpleName());

這就是日誌對象,我們通過工廠類LoggerFactory創建,傳入的類提供了日子的名字(也提供了一個直接提供字符串的重載方法)。
日誌對象存在和java對象繼承體系相似的繼承關係:

  1. 當日志對象的名字,即getLogger()中的內容,如果後面有一個日誌對象取名以改名字後面加一個點(.)加上其他內容的,那麼簽名的稱爲祖先,後面的爲繼承者,如果繼承者未設置日誌級別,那麼其就是祖先的日誌級別
  2. 如果一個日誌對象在其和其子日誌對象都沒有祖先日誌對象,那麼其就是父日誌對象。

所有的日誌對象都是根日誌對象的繼承者。
一個日誌對象有一個日誌級別,該級別可以通過配置文件設置或者使用Logger.setLevel()方法在代碼中覆寫配置文件。
日誌級別從小到大一次爲:TRACE,DEBUG,INFO,WARN,以及ERROR,每個級別都有相應的方法輸出該級別的日誌信息。
如果一個日誌對象未指定日誌級別,從最近的祖先處繼承。 根日誌對象默認級別爲DEBUG。

5.2 使用日誌對象

通過一個實際例子理解上面的描述:

	//繼承關係例子
    private static void testParentHierarchy() {
        //注意該Logger是ch.qos.logback.classic.Logger, 因爲slf4j中的logger對象是沒有setLevel方法的,以下都是該對象
        Logger parentLogger =
                (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("com.baeldung.logback");
        parentLogger.setLevel(Level.INFO);

        //根據取名規則,該日誌對象是.以前的是其符日誌對象,所以該初始化的日誌級別也是INFO
        Logger childLogger =
                (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("com.baeldung.logback.tests");

        parentLogger.warn("This message is logged because WARN > INFO");
        //設置了info級別的日誌,所以debug無法打印出
        parentLogger.debug("This message is not logged because DEBUG < INFO");
        childLogger.info("INFO == INFO");
        //該日誌對象繼承了父類的,所以debug級別的日誌無法打印
        childLogger.debug("DEBUG < INFO");
    }
//output:
17:21:46.703 [main] WARN  com.baeldung.logback -This message is logged because WARN > INFO
17:21:46.707 [main] INFO  com.baeldung.logback.tests -INFO == INFO
	//root日誌對象例子
    private static void testRootHierarchy() {
        Logger logger =
                (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("com.baeldung.logback");
        logger.debug("Hi there!");
        //rootLogger默認的日誌級別是debug
        Logger rootLogger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);

        rootLogger.debug("This message is logged because DEBUG == DEBUG");

        rootLogger.setLevel(Level.ERROR);
        //已經將日誌級別改爲error,所以warn級別的不會被打印出來
        rootLogger.warn("This message is not logged because WARN < ERROR");
        rootLogger.error("This is logged");
    }
//output:
17:23:47.136 [main] DEBUG com.baeldung.logback -Hi there!
17:23:47.140 [main] DEBUG ROOT -This message is logged because DEBUG == DEBUG
17:23:47.140 [main] ERROR ROOT -This is logged

5.3 參數化消息

通過例子來說明:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author hexin
 * @version 1.0 2018/11/13
 */
public class ParameterizeMessage {
    private final static Logger logger = LoggerFactory.getLogger(ParameterizeMessage.class);

    public static void main(String[] args) {
        String message = "This is a string";
        Integer zero = 0;
        try {
            logger.debug("Logging message: {}", message);
            logger.debug("Going to divide {} by {}", 42, zero);
            int result = 42 / zero;
        } catch (Exception e) {
			//對於異常對象e,沒有使用大括號佔位符
            logger.error("Error dividing {} by {}", 42, zero, e);
        }
    }
}
//output:
17:26:37.152 [main] DEBUG c.h.l.logback.ParameterizeMessage -Logging message: This is a string
17:26:37.157 [main] DEBUG c.h.l.logback.ParameterizeMessage -Going to divide 42 by 0
17:26:37.158 [main] ERROR c.h.l.logback.ParameterizeMessage -Error dividing 42 by 0
java.lang.ArithmeticException: / by zero
	at com.hexin.learning.logback.ParameterizeMessage.main(ParameterizeMessage.java:24)

不需要直接通過log.debug("Current count is " + count);這種字符串的拼接方式,logback支持消息參數化,即通過大括號{}提供一個佔位符,直接通過參數的形式將各種格式的內容傳入進去,對於Object對象,會調用該對象的toString()方法填充佔位符,同時對於異常信息,也可以直接打印出堆棧信息。

6. 詳細配置(Detailed Configuration)

在前面第4部分的例子中,我們使用創建了一個11行的配置文件打印日誌到控制檯。這個是logback的默認行爲:如果不能夠找到配置文件,將創建一個與根日誌(root logger)對象相關的控制檯輸出對象(ConsoleAppender)。

6.1 定位配置信息

配置文件可以放在classpath下並且取名爲logback.xml或者logback-test.xml。
以下是logback配置文件的讀取規則:

  1. 在classpath下依次(依舊是優先級由大到小)搜索配置文件logback-test.xml、logback.groovy或者logback.xml。
  2. 如果logback庫沒有找到1中的文件,將會嘗試使用java的ServiceLoader去定位com.qos.logback.classic.spi.Configurator的實現器(implementor)。
  3. 配置直接打印日誌到控制檯

注意:當前的logback版本不支持groovy配置,因爲目前還沒有兼容java9的groovy

6.2 基本配置

讓我們再次詳細看下例子中的配置。
整個配置內容都在標籤中。

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
        <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
</appender>

以上標籤中,定義了一個名叫STDOUT的ConsoleAppender類型的appender。內部有一個encoder標籤,看起來像printf風格的佔位符模板代碼。

<root level="debug">
    <appender-ref ref="STDOUT" />
</root>

以上是root標籤,該標籤設置了root日誌爲DEBUG模式,並將其關聯到名爲SDTOUT的appender中。

6.3 配置文件常見問題

logback配置文件可能變得很複雜,因此內建了一些問題解決機制。
可以通過開啓debug日誌打印模式可以觀察logback解析配置的debug信息。

<configuration debug="true">
  ...
</configuration>

logback在解析配置信息時會打印狀態信息到控制檯上:

23:54:23,040 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Found resource [logback-test.xml] at [file:/Users/egoebelbecker/ideaProjects/logback-guide/out/test/resources/logback-test.xml]
23:54:23,230 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch.qos.logback.core.ConsoleAppender]
23:54:23,236 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [STDOUT]
23:54:23,247 |-INFO in ch.qos.logback.core.joran.action.NestedComplexPropertyIA - Assuming default type [ch.qos.logback.classic.encoder.PatternLayoutEncoder] for [encoder] property
23:54:23,308 |-INFO in ch.qos.logback.classic.joran.action.RootLoggerAction - Setting level of ROOT logger to DEBUG
23:54:23,309 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [STDOUT] to Logger[ROOT]
23:54:23,310 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - End of configuration.
23:54:23,313 |-INFO in ch.qos.logback.classic.joran.JoranConfigurator@5afa04c - Registering current configuration as safe fallback point

如果解析配置文件發生警告(warnings)或者錯誤(errors)時,logback會將狀態信息打印到控制檯上。
第二種打印狀態信息的方式如下:

<configuration>
    <statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />  
    ...
</configuration>

StatusListener會攔截狀態信息,會在解析配置文件以及程序運行的時候打印信息。
打印所有的配置文件信息使得很容易定位到“流氓”配置文件。

6.4 自動重新加載配置

在程序運行中重新加載配置是一種很強的問題解決工具。通過以下配置可以實現該功能:

<configuration scan="true">
  ...
</configuration>

默認的自動掃描配置文件的間隔時間是60秒,可以指定scanPeriod的值手動設置掃描間隔時間,如下:

<configuration scan="true" scanPeriod="15 seconds">
  ...
</configuration>

可指定的時間有milliseconds,seconds,minutes或者hours。

6.5 修改loggers

在第4部分中,我們設置了root logger的級別,同時將其關聯到了console appender上。
我們可以對任何logger設置級別:

<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    <logger name="com.baeldung.logback" level="INFO" /> 
    <logger name="com.baeldung.logback.tests" level="WARN" /> 
    <root level="debug">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

添加到classpath並運行如下代碼:

Logger foobar = 
  (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("com.baeldung.foobar");
Logger logger = 
  (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("com.baeldung.logback");
Logger testslogger = 
  (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("com.baeldung.logback.tests");
 
foobar.debug("This is logged from foobar");
logger.debug("This is not logged from logger");
logger.info("This is logged from logger");
testslogger.info("This is not logged from tests");
testslogger.warn("This is logged from tests");

//output:
00:29:51.787 [main] DEBUG com.baeldung.foobar - This is logged from foobar
00:29:51.789 [main] INFO com.baeldung.logback - This is logged from logger
00:29:51.789 [main] WARN com.baeldung.logback.tests - This is logged from tests

沒有在配置文件中指定的logger,比如上面的com.baeldung.foobar會直接繼承root logger指定的級別。
logger也從root logger中繼承appender-ref。

6.6 變量替換

logback配置支持定義變量。可以在配置腳本中或者外部定義變量。
變量可以在配置腳本中的任意一個位置代表一個值。
例如,下面是一個FileAppender的配置:

<property name="LOG_DIR" value="/var/log/application" />
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>${LOG_DIR}/tests.log</file>
    <append>true</append>
    <encoder>
        <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
    </encoder>
</appender>

在配置的頂部,我們定義了一個LOG_DIR屬性,然後我們將其作爲配置路徑的一部分。
屬性在標籤中定義,但是在外部資源中也是有效的,比如系統屬性。也可以在命令行中指定屬性:

$ java -DLOG_DIR=/var/log/application com.baeldung.logback.LogbackTests

我們通過使用${propertyname}使用定義的屬性值,logback會識別該變量佔位符並使用文本替換到指定的部分,在配置腳本中的任意位置都可以識別。

7. Appenders

logger會將日誌事件(LoggingEvents)到appenders,appenders會處理真正的日誌工作。日誌通常被寫入文件或者在控制檯中打印出來,但是logback可以做更多。logback-core提供了幾個有用的appenders。

7.1 ConsoleAppender

第4部分中,已經見過該appender了,正如名字那樣說的,ConsoleAppender將信息寫入System.out或者Sytem.err中。
使用OuputStreamWriter緩存到I/O,因此直接導引到System.err不會導致非緩存寫。

7.2 FileAppender

FileAppender將日誌信息寫入到文件中。支持一些列配置參數。以下是一個配置例子:

<configuration debug="true">
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <!-- encoders are assigned the type
             ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
 
    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>tests.log</file>
        <append>true</append>
        <encoder>
            <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
        </encoder>
    </appender>
     
    <logger name="com.baeldung.logback" level="INFO" /> 
    <logger name="com.baeldung.logback.tests" level="WARN"> 
        <appender-ref ref="FILE" /> 
    </logger> 
 
    <root level="debug">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

FileAppender通過標籤配置文件名。標籤指定了是使用追加日誌的方式而不是刪除重建。如果運行幾次程序,我們就可以發現是在同一個日誌文件追加寫入的。
如果使用上面的配置文件重新運行一次,我們會發現com.baeldung.lgoback.test的日誌會同時打印在控制檯以及寫入test.log日誌文件中。也就是說子類logger會繼承root logger關聯的配置。Appenders的繼承會累積的(也就是說可以往子類一直傳)。
可以通過配置additivity爲false禁用默認行爲,就不會繼承root logger的配置了,同時其子類也不會繼承root logger。

<logger name="com.baeldung.logback.tests" level="WARN" additivity="false" > 
    <appender-ref ref="FILE" /> 
</logger> 
 
<root level="debug">
    <appender-ref ref="STDOUT" />
</root>

7.3 RollingFileAppender

將日誌追加到同一個日誌文件經常不是我們想要的。我們希望根據時間、日誌文件大小或者兩者結合來“滾動”文件。

<property name="LOG_FILE" value="LogFile" />
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${LOG_FILE}.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <!-- daily rollover -->
        <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.gz</fileNamePattern>
 
        <!-- keep 30 days' worth of history capped at 3GB total size -->
        <maxHistory>30</maxHistory>
        <totalSizeCap>3GB</totalSizeCap>
    </rollingPolicy>
    <encoder>
        <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
    </encoder>
</appender> 

RollFileAppender有一個RollgingPolicy(滾動策略)。在上面的例子中,我們使用了TimeBasedRollingPoligy,和FileAppender詳細,我們配置了一個文件名,在這裏又定義了一個佔位屬性,因爲可以複用這個名字。
在RollingPolicy中定義了fileNamePattern,該模板不僅僅是定義了文件名,同時也指定了滾動策略。TimeBasedRollingPolicy會檢查該模板,在配置的回滾模板條件下進行文件的滾動。
例如:

<property name="LOG_FILE" value="LogFile" />
<property name="LOG_DIR" value="/var/logs/application" />
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${LOG_DIR}/${LOG_FILE}.log</file> 
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <fileNamePattern>${LOG_DIR}/%d{yyyy/MM}/${LOG_FILE}.gz</fileNamePattern>
        <totalSizeCap>3GB</totalSizeCap>
    </rollingPolicy>

當前活躍的文件是/var/logs/application/LogFile(也就是日誌正寫入的文件)。該文件會在每個月的開始進行滾動操作,將其壓縮爲``/Current Year/Current Month/LogFile.gz,同時RollingFileAppender會創建一個新的活躍文件用於當前以後的日誌寫入。
當總的日誌歸檔文件到達3GB時,RollingFileAppender會刪除最早的哪一個滾動的文件。RollingFileAppender也支持內建的文件壓縮功能,當我們指定的歸檔文件類型的後綴爲壓縮格式時,會自動對滾動日誌文件進行壓縮,比如上面的LogFile.gz。
TimeBasedPolicy不是我們唯一的滾動類型。Logback也提供了SizeAndTimeBasedPolicy,根據日誌文件大小以及時間進行日誌滾動操作。也有FixedWindowRollingPolicy,每次日誌啓動時會進行滾動。
也可以自行實現RollingPolicy。

7.4 定製Appenders

可以通過繼承logback的基礎appender類中的一個實現定製化的appenders,可以查閱相關文檔。

8. layouts

layout負責日誌消息的格式化。像logback的其他部分一樣,layouts也是可擴展的。
默認的PatternLayout提供了大部分需要的格式。
如下是一個例子:

<encoder>
    <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>

該配置腳本包含了PatternLayoutEncoder的配置,傳遞encoder給appender,encoder使用PatternLayout對消息進行格式化。
標籤中的內容定義了消息的格式化模板。PatternLayout實現了非常多的約定的格式與創建模板的修飾符。
以以上例子爲例,PatternLayout通過一個%識別約定格式字符:

  • %d{HH:mm:ss.SSS}——指定了小時、分鐘、秒以及毫秒的時間
  • [%thread]——輸出日誌的線程名,已方括號包圍
  • %-5level——日誌時間的輸出級別,填充到5個字符
  • %logger{36}——logger的名字,縮減到35個字符
  • %msg%n——日誌的信息後面跟上對應系統的換行符

日誌輸出示例:

21:32:10.311 [main] DEBUG com.baeldung.logback.LogbackTests - Logging message: This is a String

其他的格式化模板符號參看:https://logback.qos.ch/manual/layouts.html#conversionWord

原文地址:https://www.baeldung.com/logback

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章