Java的Log架構(Log4j2 + Slf4j)

Zlase發表於2020-12-03

目錄

常見的日誌門面

常見的日誌實現

日誌框架介紹

JUL

Log4j

LogBack

Log4j2

SLF4J的使用

簡介

slf4j-api 和 slf4j-simple 使用

Log4j2的使用

簡介

Demo

非同步日誌(效率提升的原因)

使用Log4j2和slf4j的日誌架構

包引入

Domo實現

Log4j2的配置檔案

Log4j2配置非同步日誌

Log4j2無垃圾機制


常見的日誌門面

JCL,slf4j(Simple Logging For java)

 

常見的日誌實現

JUL,log4j,logback,log4j2

 

日誌框架介紹

JUL

Java自帶的日誌框架,不需要引入,直接可以使用

Log4j

非常經典的日誌框架,但是已經停止更新和維護了

LogBack

logBack和log4j不是同一個公司開發的,LogBack是Spring自動整合的日誌框架了。目前比較流行

Log4j2

Log4j2是Log4j的進階版,相比於LogBack,在非同步的時候速度可以快近10倍,目前Spring專案中,都會把自動引入的LogBack註釋掉,改用Log4j2. 未來使用Log4j2+slf4j這套組合一定是未來的幾年的主流日誌框架。

 

SLF4J的使用

簡介

SLF4J(Simple Logging Facade For Java)是給Java日誌訪問提供的一套標準,具體的實現,主要交給log4j,logback,log4j2等日誌框架。SLF4J其實也實現了簡單的日誌實現,但是幾乎不會有人去使用,而日誌框架一般會選擇slf4j-api作為門面,配上具體的實現框架,中間使用連線橋(介面卡)來完成日誌框架的建立。 slf4j-api 和 slf4j-simple 引入。 api是日誌門戶,simple是日誌的簡單實現。

slf4j-api 和 slf4j-simple 使用

log的級別有trace,debug,info,warn,error和fatal六個級別,預設情況下,只會輸出info,warn和error,fatal四個級別的日誌。 佔位符的使用,可以使用{}作為佔位符,後邊跟具體的變數,只要能使用佔位符就一定要使用這種模式作為日誌模式,但是需要小心注入攻擊。 使用slf4j-api 和 slf4j-simple的方式輸出日誌,不需要匯入中間的介面卡,當多個log實現框架同時存在的時候,會預設先使用slf4j-simple框架。

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
​
public class Slf4jTest {
    private static final Logger LOGGER = LoggerFactory.getLogger(Slf4jTest.class);
​
    @Test
    public void test01() {
        // 日誌輸出,預設會輸出info級別以上的日誌
        LOGGER.error("error");
        LOGGER.warn("warn");
        LOGGER.info("info");
        LOGGER.debug("debug");
        LOGGER.trace("trace");
​
        // 使用佔位符輸出日誌資訊
        String name = "Peter";
        Integer age = 10;
        LOGGER.info("User:{}, Age:{}", name, age);
    }
​
}

 

Log4j2的使用

簡介

        Apache Log4j2 是 log4j 的升級版,參考了logback的優秀設計,優化並解決了很多問題: 異常處理,在logback中,Appender中的異常不會被應用感知到,但是在log4j2中提供了一些異常處理機制。 效能提升,log4j2相比於logback和log4j在效能上有了很大的提升。 自動過載配置,提供了自動重新整理引數配置,可以再生產上動態修改日誌的級別而不需要重啟應用。 無垃圾機制,log4j2在大部分情況下,使用自動垃圾清收機制,避免日誌頻繁的GC。 Log4j2 也可以做為日誌的門面,但是因為其實現功能強大,但是門面略弱,所以當前主流的日誌框架都是使用slf4j作為日誌的門面,使用Log4j2作為日誌的實現,通過聯結器繫結,進行工作。 使用log4j-api和log4j-core的日誌架構如下:

依賴資訊:
<!--匯入日誌門面-->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.13.3</version>
</dependency>
​
<!--匯入日誌實現-->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.13.3</version>
</dependency>

Demo

package logger;
​
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.Test;
​
public class Log4j_Api_Core {
    private static final Logger LOGGER = LogManager.getLogger(Log4j_Api_Core.class);
​
    @Test
    public void test02(){
        // 日誌輸出,預設會輸出info級別以上的日誌
        LOGGER.fatal("fatal");
        LOGGER.error("error");
        LOGGER.warn("warn");
        LOGGER.info("info");
        LOGGER.debug("debug");
        LOGGER.trace("trace");
​
        // 使用佔位符輸出日誌資訊
        String name = "Peter";
        Integer age = 10;
        LOGGER.info("User:{}, Age:{}", name, age);
    }
​
}

非同步日誌(效率提升的原因)

同步日誌:

正常的日誌,線上程需要列印日誌的時候,會走一套流程,初始化,並生成日誌,判斷等級是否過濾等等,最後決定是否輸出非同步日誌:

非同步日誌

非同步日誌的使用方式是在主執行緒需要生成日誌之後,就將資訊傳遞給一個阻塞的訊息佇列ArrayBlockingQueue,這個佇列將啟動一個(或多個)新的執行緒,完成後續日誌的生成,所以效率高了很多

效能圖表:

通常使用全域性非同步日誌的效率最高,使用非同步日誌和同步日誌混合的效率其次,使用同步日誌的效率最低。通常情況下,都會直接使用非同步日誌提高效率。

 

使用Log4j2和slf4j的日誌架構

當前最主流的日誌使用方式事Log4j2作為實現,slf4j作為門戶。

包引入

這個組合需要引入4個包,分別為Log4j2的門戶和實現,slf4j的門戶,slf4j和Log4j2的聯結器。 之後就可以按照slf4j的所有實現模式和門戶模式進行操作了。

<!--匯入slf4j日誌門面-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.30</version>
</dependency>
​
<!--匯入log4j2日誌實現-->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.13.3</version>
</dependency>
<!--匯入log4j2日誌門面-->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.13.3</version>
</dependency>
​
<!--使用介面卡-->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j-impl</artifactId>
    <version>2.9.1</version>
</dependency>

Domo實現

使用slf4j和log4j2的組合,Demo如下:

package logger;
​
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
​
public class Slf4j_Log4j2 {
    private static final Logger LOGGER = LoggerFactory.getLogger(Slf4j_Log4j2.class);
​
    @Test
    public void test03() {
        // 日誌輸出,預設會輸出info級別以上的日誌
        LOGGER.error("error");
        LOGGER.warn("warn");
        LOGGER.info("info");
        LOGGER.debug("debug");
        LOGGER.trace("trace");
​
        // 使用佔位符輸出日誌資訊
        String name = "Peter";
        Integer age = 10;
        LOGGER.info("User:{}, Age:{}", name, age);
    }
​
}

Log4j2的配置檔案

log4j2 預設會載入在resources下的xml配置檔案,名稱為log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
​
<!--status = debug 日誌框架本身的輸出的日誌級別,不是我們定義的日誌,而是框架自身日誌輸出的級別-->
<!--monitorInterval 自動載入配置檔案的間隔時間,不低於5s,可以實現熱更新-->
<Configuration status="debug" monitorInterval="5">
    <!-- 集中配置屬性進行管理  可以定義各種名稱和對應的值 -->
    <properties>
        <property name="LOG_HOME">E:/logs</property>
        <property name="FILE_NAME">mylog</property>
        <property name="log.sql.level">info</property>
    </properties>
​
    <!--日誌處理-->
    <Appenders>
        <!--控制檯輸出結果 appender-->
        <!--target 為日誌的等級,如果為 SYSTEM_OUT 是普通日誌,為白色;如果是SYSTEM_ERR,則為報警顏色,紅色-->
        <Console name="Console" target="SYSTEM_OUT">
            <!--日誌輸出的格式-->
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %l - %msg%n"/>
        </Console>
​
        <!-- 日誌的檔案輸出 -->
        <File name="file" fileName="${LOG_HOME}/myfile.log">
            <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n"/>
        </File>
​
        <!--隨機讀寫流實現檔案日誌的輸出,效能得到了提高,功能和 name = file是一樣的-->
        <RandomAccessFile name="accessFile" fileName="${LOG_HOME}/myAcclog.log">
            <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n"/>
        </RandomAccessFile>
​
        <!--按照一定的規則拆分日誌檔案的(比如按照天,按照小時拆分日誌) appender-->
        <RollingRandomAccessFile name="RollingRandomAccessFile" fileName="${LOG_HOME}/${FILE_NAME}.log"
                                 filePattern="${LOG_HOME}/$${date:yyyy-MM}/${FILE_NAME}-%d{yyyy-MM-dd HH-mm}-%i.log">
            <!--日誌級別的過濾器-->
            <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n"/>
            <Policies>
                <!--在系統啟動時,觸發拆分規則,生產一個新的日誌檔案-->
                <OnStartupTriggeringPolicy/>
                <!--按照時間的節點進行拆分,規則根據filePattern定義-->
                <TimeBasedTriggeringPolicy interval="1"/>
                <!--按照10Mb大小為一個檔案的上限-->
                <SizeBasedTriggeringPolicy size="10 MB"/>
            </Policies>
            <!--最多可以生成多少個日誌檔案,超出之後,最舊的會被覆蓋-->
            <DefaultRolloverStrategy max="20"/>
        </RollingRandomAccessFile>
    </Appenders>
​
    <!--Logger定義-->
    <Loggers>
        <!--日誌輸出的等級,超過該等級的進行輸出-->
        <Root level="info">
            <!--指定日誌的處理器-->
            <AppenderRef ref="Console"/>
            <AppenderRef ref="RollingRandomAccessFile"/>
            <AppenderRef ref="file"/>
            <AppenderRef ref="accessFile"/>
        </Root>
    </Loggers>
​
</Configuration>

Log4j2配置非同步日誌

非同步配置有兩種方式,全域性非同步和區域性非同步(非同步和同步混合的方式)。區域性非同步的方式,如果處理不好,可能效能比完全同步效率還低,因此,僅介紹全域性非同步的使用方式。

需要引入一個新的jar包,用於非同步日誌生成;

<!--非同步log4j2日誌的依賴包-->
<dependency>
    <groupId>com.lmax</groupId>
    <artifactId>disruptor</artifactId>
    <version>3.3.4</version>
</dependency>

之後在resources下,定義一個檔案 log4j2.component.properties 在這個檔案中啟動全域性非同步日誌的指令即可:

log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector

Log4j2無垃圾機制

在log4j2的2.6版本之後,會啟動一個無垃圾機制,它會自動的清除日誌中的垃圾,減少了系統自動GC的頻率,大大提高了速度(無垃圾機制自動開啟,不需要手動操作)。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

相關文章