NET2Java之五:日誌記錄

由於更早的擁抱開源,Java在解決方案的數量上擁有.NET無法比擬的優勢,可這就苦了我們這樣的菜雞玩家,比如日誌,光主流的解決方案就有JUL、Log4j、Logback、Log4j2、SLF4j,這麼多方案,到底要用哪一個?

若是論資歷的話,log4j無疑是老前輩,作爲早期使用最廣的的日誌框架,由Ceki創建,後來成爲Apache的頂級項目之一,說是Java社區的日誌標準也不爲過,不過Sun公司可不這麼覺得,覺得Apache就是典型的關公面前耍大刀,於是在jdk1.4之後增加了一個名爲“java.util.logging”的包,簡稱JUL,用於日誌記錄。不過由於log4j已經有了廣大的羣衆基礎,加上JUL時間倉促,有些地方並不好用,所以不少人對此並不買賬。Apache一瞅,Sun也太小家子器了,決定教教Sun什麼叫格局,就推出了Jakarta Commons Logging,簡稱JCL,JCL定義了一套日誌接口,只要你調用的JCL的接口,你想用log4j用log4j,想用jul用jul,我全都要。據說是受不了Apache的工作氛圍,log4j的創始人Ceki於2006年出走Apache,並先後創建了Slf4j(類似於JCL)和logback(類似於log4j),於是Java日誌領域一分爲二:JCL和Slf4j兩大陣營。logback作爲後起之秀,船小好調頭,又有親爹加持,發展的越來越快,隱隱有些壓住log4j一頭,所以Apache在2012年又在log4j的基礎上升級爲2.0版本(賽亞人變身了屬於),也就是現在的log4j2。

其實說白了就是天才老爹入贅豪門,生了個哥哥,後來名門大戶的呆不習慣,離家創業,又生了個弟弟,親爹肯定是想弟弟繼承家業,但是大戶族老們可不答應,畢竟寶都押在了哥哥頭上,於是上演了一出兄弟相爭的戲碼,倒黴的卻是嫡出的JUL。

slf4j

官網:https://slf4j.org/

sfl4j全稱Simple Logging Facade for Java,它爲各種日誌框架(log4j、logback等)提供了一個共同的API,而非日誌的具體實現方案,所以如果需要實現日誌的輸出,還需要引入其他框架。由於slf4j將應用程序和具體的日誌實現方案解耦,可以讓開發人員在不同的日誌框架之間進行切換,比如你開始用了log4j後來發現logback更好用,只需要修改依賴和配置即可,而無需修改代碼。所以在開發時,我們在項目中要避免直接使用log4j和logback中的API,而應該依賴使用slf4j中API。如果使用了slf4j,那麼無論你使用log4j還是logback,都可以使用下面的代碼記錄日誌:

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

public class Main {
    public static void main(String[] args) {
        System.out.println("開始執行");

        Logger logger = LoggerFactory.getLogger(Main.class);

        logger.trace("跟蹤日誌");
        logger.debug("調試日誌");
        logger.info("普通日誌");
        logger.warn("報警日誌");
        logger.error("錯誤日誌");

        System.out.println("執行完畢");
    }
}

Maven依賴:

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

slf4j+log4j2

官網:https://logging.apache.org/log4j/2.x/index.html

在應用中想使用,slf4j+log4j來記錄日誌,除了需要使用slf4j-api之外,還需要額外引入適配器:

  • slf4j 1.7.x及其之前的版本使用log4j-slf4j-impl
  • slf4j 2.0.x及其之後的版本使用log4j-slf4j2-impl

項目依賴:

<dependencies>
    <!-- SLF4J API -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.30</version> 
    </dependency>
    
    <!-- Log4j2 API -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-api</artifactId>
        <version>2.14.1</version>
    </dependency>
    
    <!-- Log4j2 Core Implementation -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.14.1</version> 
    </dependency>
    
    <!-- SLF4J Log4j2 Binding -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-slf4j-impl</artifactId>
        <version>2.14.1</version>
    </dependency>
</dependencies>

不過一般情況下由於log4j-corelog4j-slf4j-impl中已經自動依賴了log4j-apislf4j-api,所以有時候依賴會簡寫爲:

<dependencies>        
    <!-- Log4j2 Core Implementation -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.14.1</version> 
    </dependency>
    
    <!-- SLF4J Log4j2 Binding -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-slf4j-impl</artifactId>
        <version>2.14.1</version>
    </dependency>
</dependencies>

如果你需要使用log4j 1.x,那麼可以使用log4j-to-slf4j用於log4j 1.x和slf4j之間橋接。

log4j的配置可通過編程方式和配置文件的方式實現,主流的方式都是使用配置文件:

  • log4j 2配置文件的名稱爲log4j2.mxl,放在src目錄下,maven項目放在resources下。
  • log4j 2也支持json、yaml等格式的配置文件,如log4j2.properties、log4j2.yaml、log4j2.json等

在resources下面新建log4j2.xml文件。

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

<Configuration status="error">
    <!--定義變量-->
    <Properties>
        <!--定義日誌存儲目錄的變量-->
        <Property name="logDir">D:/logs</Property>
    </Properties>
    <!--輸出源-->
    <Appenders>
        <!--輸出到控制檯-->
        <Console name="Console" target="SYSTEM_OUT">
            <!--可選配置:控制檯只輸出level及以上級別的信息,其他的直接拒絕,具體的可以查看ThresholdFilter相關的說明-->
            <ThresholdFilter level="INFO" onMatch="ACCEPT"/>
            <!--日誌的輸出格式-->
            <PatternLayout pattern="[%-level]%d{yyyy-MM-dd HH:mm:ss.SSS} %logger{36} - %msg%n"/>
        </Console>
        <!--輸出到文件,大部分時候都會使用RollingFile-->
        <File name="File" fileName="${logDir}/log.txt">
            <PatternLayout>
                <Pattern>%d %p %c [%t] %m%n</Pattern>
            </PatternLayout>
        </File>

        <!-- 滾動輸出:
            name:Appender的唯一標識,跟下面AppenderRef中的ref對應。
            fileName:指定了日誌文件當前活動路徑和文件名,決定了日誌文件被寫入的初始文件位置。
            filePattern:定義滾動後的文件命名規則
         -->
        <RollingFile name="RollingFileInfo" fileName="d:/logs/log.txt" filePattern="d:/logs/log-%d{yyyy-MM-dd}_%i.txt">
            <!-- 只輸出level及以上級別的信息(onMatch),其他的直接拒絕(onMismatch) -->
            <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
            <!-- 輸出的格式  -->
            <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss,SSS}] [%p] - %l - %m%n"/>
            <!-- Policies:滾動策略,決定何時應該產生新日誌 -->
            <Policies>
                <!--時間策略:具體的看文件命名格則,如{yyyy-MM-dd},那這個意思就是每天一個,如果是{yyyy-MM-dd HH}那這個意思就是每小時,以此類推-->
                <TimeBasedTriggeringPolicy interval="1"/>
                <!--大小策略:每當文件大小達到500MB,就開始滾動,產生一個新日誌文件,後面的%i即標識產生了第幾個文件-->
                <SizeBasedTriggeringPolicy size="500MB"/>
            </Policies>
            <!--文件夾下最多可以有幾個文件,過期的舊文件會被刪除,默認是7-->
            <DefaultRolloverStrategy max="30"/>
        </RollingFile>
    </Appenders>
    <!-- 記錄器 -->
    <Loggers>
        <!--
         name:指定logger對象的名稱,用於區別日誌記錄的來源
         level:debug、info、warn、error、fatal,按重要程度從低到高,如果配置爲warn,那麼就只有warn、error、fatal的日誌會被記錄
         additivity:用於指定日誌消息是否要向上傳遞給父級日誌記錄器 -->
        <Logger name="em.im.pve" level="debug" additivity="true"/>

        <!-- 日誌系統中的最頂層Logger,代表了日誌層次結構的根節點,指定使用哪些Appender -->
        <Root level="info">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="RollingFileInfo"/>
        </Root>
    </Loggers>
</Configuration>

Appenders中遠不止控制檯和文件這兩種形式,還包括數據庫、MQ、HTTP等多種輸出形式,但業務上最常用的還是輸出到文件,如果有其他的輸出需求,再去官方閱讀文檔即可。不過我覺得對大多數人來說,困惑最多的可能還是Layout的使用,比如:[%d{yyyy-MM-dd HH:mm:ss,SSS}] [%p] - %l - %m%n這到底是個啥?且聽我一一拆解:

  • %d{yyyy-MM-dd HH:mm:ss.SSS}:就是表示格式化輸出的時間,如果你就是示例的輸出,一個%d就行。
  • [ ]就是字面意思,不用糾結。
  • %level:日誌級別和%p表示的結果一樣。
  • %pid:進程id
  • %-5P:-表示左對齊,5表示佔位空間
  • %L:輸出行號
  • %m:日誌內容
  • %n:換行,一般就是一條日誌一行
  • %t:輸入製表符
  • %logger:和%c的意思一樣,輸出日誌記錄器的名稱,後面緊跟的數字是精度,以減小記錄器名稱的大小

slf4j+logback

官網:https://logback.qos.ch/

logback中有三個主要的核心模塊:logback-core、logback-classic、logback-access

  • logback-core:核心模塊,提供了日誌框架的基礎功能,是我們通常意義上說的logback。
  • logback-classic:實現了slf4j api。
  • logback-access:用於日誌訪問,可以通過HTTP訪問日誌信息。

由於logback-classic中已經引入了logbak-core和slf4j-api,所以直接引入logback-classic即可:


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

配置文件(logback.xml):

<?xml version="1.0" encoding="UTF-8"?>
<!--配置根:
        scan:設置爲true是,如果配置文件發生更改,配置就會重新加載
        scanPeriod:當scan爲true時,會依據這個進行掃描查看是不是更新,默認單位是ms
-->
<configuration scan="true" scanPeriod="60 seconds">
    <!-- 變量定義 -->
    <property name="logPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5level %c{36} - %msg%n"/>
    <property name="logPath" value="d:/logs/log.txt" />

    <!-- 控制檯輸出 -->
    <appender name="logConsole" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${logPattern}</pattern>
        </encoder>
    </appender>
    <!-- 文件輸出,記錄INFO級別以上的日誌 -->
    <appender name="logFileInfo" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${logPath}</file>
        <encoder>
            <pattern>${logPattern}</pattern>
        </encoder>
        <!-- 滾動策略:當然還有FixedWindowRollingPolicy、SizeBasedTriggeringPolicy等策略-->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--滾動輸出的路徑-->
            <fileNamePattern>log/app-%d{yyyy-MM-dd}.txt</fileNamePattern>
            <!--文件夾下保留文件的最大數量-->
            <maxHistory>30</maxHistory>
        </rollingPolicy>
    </appender>

    <!-- 根日誌級別設置爲INFO,附加到控制檯和文件輸出 -->
    <root level="INFO">
        <appender-ref ref="logConsole"/>
        <appender-ref ref="logFileInfo"/>
    </root>
</configuration>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章