【日誌】新版日誌技術

0. 學習目標

  1. 日誌門面和日誌體系
  2. SLF4J
  3. logback的使用
  4. log4j2的使用
  5. SpringBoot中的日誌使用

1. 日誌門面

當咱們的系統變的更加複雜的時候,咱們的日誌就容易發生混亂。隨着系統開發的進行,可能會更新不一樣的日誌框架,形成當前系統中存在不一樣的日誌依賴,讓咱們難以統一的管理和控制。就算咱們強制要求全部的模塊使用相同的日誌框架,系統中也難以免使用其餘相似spring,mybatis等其餘的第三方框架,它們依賴於咱們規定不一樣的日誌框架,並且他們自身的日誌系統就有着不一致性,依然會出來日誌體系的混亂。html

因此咱們須要借鑑JDBC的思想,爲日誌系統也提供一套門面,那麼咱們就能夠面向這些接口規範來開發,避免了直接依賴具體的日誌框架。這樣咱們的系統在日誌中,就存在了日誌的門面和日誌的實現。java

常見的日誌門面:
JCL、slf4j
常見的日誌實現:
JUL、log4j、logback、log4j2web

日誌門面和日誌實現的關係:
spring

日誌框架出現的歷史順序:
log4j -->JUL-->JCL--> slf4j --> logback --> log4j2數據庫

2. SLF4J的使用

簡單日誌門面(Simple Logging Facade For Java) SLF4J主要是爲了給Java日誌訪問提供一套標準、規範的API框架,其主要意義在於提供接口,具體的實現能夠交由其餘日誌框架,例如log4j和logback等。固然slf4j本身也提供了功能較爲簡單的實現,可是通常不多用到。對於通常的Java項目而言,日誌框架會選擇slf4j-api做爲門面,配上具體的實現框架(log4j、logback等),中間使用橋接器完成橋接。apache

官方網站: https://www.slf4j.org/api

SLF4J是目前市面上最流行的日誌門面。如今的項目中,基本上都是使用SLF4J做爲咱們的日誌系統。
SLF4J日誌門面主要提供兩大功能:數組

  1. 日誌框架的綁定
  2. 日誌框架的橋接

2.1 SLF4J入門

  1. 添加依賴tomcat

    <!--slf4j core 使用slf4j必須添加-->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.27</version>
    </dependency>
    <!--slf4j 自帶的簡單日誌實現 -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-simple</artifactId>
        <version>1.7.27</version>
    </dependency>
  2. 編寫代碼springboot

    public class Slf4jTest {
        // 聲明日誌對象
        public final static Logger LOGGER =
        LoggerFactory.getLogger(Slf4jTest.class);
        @Test
        public void testQuick() throws Exception {
            //打印日誌信息
            LOGGER.error("error");
            LOGGER.warn("warn");
            LOGGER.info("info");
            LOGGER.debug("debug");
            LOGGER.trace("trace");
            // 使用佔位符輸出日誌信息
            String name = "jack";
            Integer age = 18;
            LOGGER.info("用戶:{},{}", name, age);
            // 將系統異常信息寫入日誌
            try {
                int i = 1 / 0;
            } catch (Exception e) {
                // e.printStackTrace();
                LOGGER.info("出現異常:", e);
            }
        }
    }

爲何要使用SLF4J做爲日誌門面?

  1. 使用SLF4J框架,能夠在部署時遷移到所需的日誌記錄框架。
  2. SLF4J提供了對全部流行的日誌框架的綁定,例如log4j,JUL,Simple logging和NOP。所以能夠在部署時切換到任何這些流行的框架。
  3. 不管使用哪一種綁定,SLF4J都支持參數化日誌記錄消息。因爲SLF4J將應用程序和日誌記錄框架分離,所以能夠輕鬆編寫獨立於日誌記錄框架的應用程序。而無需擔憂用於編寫應用程序的日誌記錄框架。
  4. SLF4J提供了一個簡單的Java工具,稱爲遷移器。使用此工具,能夠遷移現有項目,這些項目使用日誌框架(如Jakarta Commons Logging(JCL)或log4j或Java.util.logging(JUL))到SLF4J。

2.2 綁定日誌的實現(Binding)

如前所述,SLF4J支持各類日誌框架。SLF4J發行版附帶了幾個稱爲「SLF4J綁定」的jar文件,每一個綁定對應一個受支持的框架。

使用slf4j的日誌綁定流程:

  1. 添加slf4j-api的依賴
  2. 使用slf4j的API在項目中進行統一的日誌記錄
  3. 綁定具體的日誌實現框架
    1. 綁定已經實現了slf4j的日誌框架,直接添加對應依賴
    2. 綁定沒有實現slf4j的日誌框架,先添加日誌的適配器,再添加實現類的依賴
  4. slf4j有且僅有一個日誌實現框架的綁定(若是出現多個默認使用第一個依賴日誌實現)

經過maven引入常見的日誌實現框架:

<!--slf4j core 使用slf4j必須添加-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.27</version>
</dependency>

<!-- log4j-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.27</version>
</dependency>
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

<!-- jul -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-jdk14</artifactId>
    <version>1.7.27</version>
</dependency>

<!--jcl -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-jcl</artifactId>
    <version>1.7.27</version>
</dependency>

<!-- nop -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-nop</artifactId>
    <version>1.7.27</version>
</dependency>

要切換日誌框架,只需替換類路徑上的slf4j綁定。例如,要從java.util.logging切換到log4j,只需將slf4j-jdk14-1.7.27.jar替換爲slf4j-log4j12-1.7.27.jar便可。

SLF4J不依賴於任何特殊的類裝載。實際上,每一個SLF4J綁定在編譯時都是硬連線的, 以使用一個且只有一個特定的日誌記錄框架。例如,slf4j-log4j12-1.7.27.jar綁定在編譯時綁定以使用log4j。在您的代碼中,除了slf4j-api-1.7.27.jar以外,您只需將您選擇的一個且只有一個綁定放到相應的類路徑位置。不要在類路徑上放置多個綁定。如下是通常概念的圖解說明。

2.3 橋接舊的日誌框架(Bridging)

一般,您依賴的某些組件依賴於SLF4J之外的日誌記錄API。您也能夠假設這些組件在不久的未來不會切換到SLF4J。爲了解決這種狀況,SLF4J附帶了幾個橋接模塊,這些模塊將對log4j,JCL和java.util.logging API的調用重定向,就好像它們是對SLF4J API同樣。

橋接解決的是項目中日誌的遺留問題,當系統中存在以前的日誌API,能夠經過橋接轉換到slf4j的實現

  1. 先去除以前老的日誌框架的依賴
  2. 添加SLF4J提供的橋接組件
  3. 爲項目添加SLF4J的具體實現

遷移的方式:
若是咱們要使用SLF4J的橋接器,替換原有的日誌框架,那麼咱們須要作的第一件事情,就是刪除掉原有項目中的日誌框架的依賴。而後替換成SLF4J提供的橋接器。

<!-- log4j-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>log4j-over-slf4j</artifactId>
    <version>1.7.27</version>
</dependency>

<!-- jul -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jul-to-slf4j</artifactId>
    <version>1.7.27</version>
</dependency>

<!--jcl -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jcl-over-slf4j</artifactId>
    <version>1.7.27</version>
</dependency>

注意問題:

  1. jcl-over-slf4j.jar和 slf4j-jcl.jar不能同時部署。前一個jar文件將致使JCL將日誌系統的選擇委託給SLF4J,後一個jar文件將致使SLF4J將日誌系統的選擇委託給JCL,從而致使無限循環。
  2. log4j-over-slf4j.jar和slf4j-log4j12.jar不能同時出現
  3. jul-to-slf4j.jar和slf4j-jdk14.jar不能同時出現
  4. 全部的橋接都只對Logger日誌記錄器對象有效,若是程序中調用了內部的配置類或者是Appender,Filter等對象,將沒法產生效果。

2.4 SLF4J原理解析

  1. SLF4J經過LoggerFactory加載日誌具體的實現對象。
  2. LoggerFactory在初始化的過程當中,會經過performInitialization()方法綁定具體的日誌實現。
  3. 在綁定具體實現的時候,經過類加載器,加載org/slf4j/impl/StaticLoggerBinder.class
  4. 因此,只要是一個日誌實現框架,在org.slf4j.impl包中提供一個本身的StaticLoggerBinder類,在其中提供具體日誌實現的LoggerFactory就能夠被SLF4J所加載

3. Logback的使用

Logback是由log4j創始人設計的另外一個開源日誌組件,性能比log4j要好。
官方網站:https://logback.qos.ch/index.html

Logback主要分爲三個模塊:

  • logback-core:其它兩個模塊的基礎模塊
  • logback-classic:它是log4j的一個改良版本,同時它完整實現了slf4j API
  • logback-access:訪問模塊與Servlet容器集成提供經過Http來訪問日誌的功能

後續的日誌代碼都是經過SLF4J日誌門面搭建日誌系統,因此在代碼是沒有區別,主要是經過修改配置文件和pom.xml依賴

3.1 logback入門

  1. 添加依賴

    <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>
  2. java代碼

    //定義日誌對象
    public final static Logger LOGGER = LoggerFactory.getLogger(LogBackTest.class);
    
    @Test
    public void testSlf4j(){
        //打印日誌信息
        LOGGER.error("error");
        LOGGER.warn("warn");
        LOGGER.info("info");
        LOGGER.debug("debug");
        LOGGER.trace("trace");
    }

3.2 logback配置

logback會依次讀取如下類型配置文件:

  • logback.groovy
  • logback-test.xml
  • logback.xml 若是均不存在會採用默認配置
  1. logback組件之間的關係
    1. Logger:日誌的記錄器,把它關聯到應用的對應的context上後,主要用於存放日誌對象,也能夠定義日誌類型、級別。
    2. Appender:用於指定日誌輸出的目的地,目的地能夠是控制檯、文件、數據庫等等。
    3. Layout:負責把事件轉換成字符串,格式化的日誌信息的輸出。在logback中Layout對象被封裝在encoder中。
  2. 基本配置信息

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
        <!--
            日誌輸出格式:
            %-5level
            %d{yyyy-MM-dd HH:mm:ss.SSS}日期
            %c類的完整名稱
            %M爲method
            %L爲行號
            %thread線程名稱
            %m或者%msg爲信息
            %n換行
        -->
        <!--格式化輸出:%d表示日期,%thread表示線程名,%-5level:級別從左顯示5個字符寬度,%msg:日誌消息,%n是換行符-->
        <property name="pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %c [%thread] %-5level %msg%n"/>
        <!--
            Appender: 設置日誌信息的去向,經常使用的有如下幾個
            ch.qos.logback.core.ConsoleAppender (控制檯)
            ch.qos.logback.core.rolling.RollingFileAppender (文件大小到達指定尺寸的時候產生一個新文件)
            ch.qos.logback.core.FileAppender (文件)
        -->
        <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
            <!--輸出流對象 默認 System.out 改成 System.err-->
            <target>System.err</target>
            <!--日誌格式配置-->
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>${pattern}</pattern>
            </encoder>
        </appender>
        <!--
            用來設置某一個包或者具體的某一個類的日誌打印級別、以及指定<appender>。
            <loger>僅有一個name屬性,一個可選的level和一個可選的addtivity屬性
            name:
                用來指定受此logger約束的某一個包或者具體的某一個類。
            level:
                用來設置打印級別,大小寫無關:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
                若是未設置此屬性,那麼當前logger將會繼承上級的級別。
            additivity:
                是否向上級loger傳遞打印信息。默認是true。
            <logger>能夠包含零個或多個<appender-ref>元素,標識這個appender將會添加到這個logger
        -->
        <!--
            也是<logger>元素,可是它是根logger。默認debug
            level:用來設置打印級別,大小寫無關:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
            <root>能夠包含零個或多個<appender-ref>元素,標識這個appender將會添加到這個logger。
        -->
        <root level="ALL">
            <appender-ref ref="console"/>
        </root>
    </configuration>
  3. FileAppender配置

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
        <!-- 自定義屬性 能夠經過${name}進行引用-->
        <property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss} %c %M %L [%thread] %m %n"/>
        <!--
        日誌輸出格式:
            %d{pattern}日期
            %m或者%msg爲信息
            %M爲method
            %L爲行號
            %c類的完整名稱
            %thread線程名稱
            %n換行
            %-5level
        -->
        <!-- 日誌文件存放目錄 -->
        <property name="log_dir" value="d:/logs"></property>
    
        <!--控制檯輸出appender對象-->
        <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
            <!--輸出流對象 默認 System.out 改成 System.err-->
            <target>System.err</target>
            <!--日誌格式配置-->
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>${pattern}</pattern>
            </encoder>
        </appender>
    
        <!--日誌文件輸出appender對象-->
        <appender name="file" class="ch.qos.logback.core.FileAppender">
            <!--日誌格式配置-->
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>${pattern}</pattern>
            </encoder>
            <!--日誌輸出路徑-->
            <file>${log_dir}/logback.log</file>
        </appender>
    
        <!-- 生成html格式appender對象 -->
        <appender name="htmlFile" class="ch.qos.logback.core.FileAppender">
            <!--日誌格式配置-->
            <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
                <layout class="ch.qos.logback.classic.html.HTMLLayout">
                    <pattern>%level%d{yyyy-MM-dd HH:mm:ss}%c%M%L%thread%m</pattern>
                </layout>
            </encoder>
            <!--日誌輸出路徑-->
            <file>${log_dir}/logback.html</file>
        </appender>
    
        <!--RootLogger對象-->
        <root level="all">
            <appender-ref ref="console"/>
            <appender-ref ref="file"/>
            <appender-ref ref="htmlFile"/>
        </root>
    </configuration>
  4. RollingFileAppender配置

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
        <!-- 自定義屬性 能夠經過${name}進行引用-->
        <property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss} %c %M %L [%thread] %m %n"/>
        <!--
        日誌輸出格式:
            %d{pattern}日期
            %m或者%msg爲信息
            %M爲method
            %L爲行號
            %c類的完整名稱
            %thread線程名稱
            %n換行
            %-5level
        -->
        <!-- 日誌文件存放目錄 -->
        <property name="log_dir" value="d:/logs"></property>
    
        <!--控制檯輸出appender對象-->
        <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
            <!--輸出流對象 默認 System.out 改成 System.err-->
            <target>System.err</target>
            <!--日誌格式配置-->
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
            </encoder>
        </appender>
    
        <!-- 日誌文件拆分和歸檔的appender對象-->
        <appender name="rollFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <!--日誌格式配置-->
            <encoder
            class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>${pattern}</pattern>
            </encoder>
            <!--日誌輸出路徑-->
            <file>${log_dir}/roll_logback.log</file>
            <!--指定日誌文件拆分和壓縮規則-->
            <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
                <!--經過指定壓縮文件名稱,來肯定分割文件方式-->
                <fileNamePattern>${log_dir}/rolling.%d{yyyy-MMdd}.log%i.gz</fileNamePattern>
            <!--文件拆分大小-->
                <maxFileSize>1MB</maxFileSize>
            </rollingPolicy>
        </appender>
    
        <!--RootLogger對象-->
        <root level="all">
            <appender-ref ref="console"/>
            <appender-ref ref="rollFile"/>
        </root>
    </configuration>
  5. Filter和異步日誌配置

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
        <!-- 自定義屬性 能夠經過${name}進行引用-->
        <property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss} %c %M %L [%thread] %m %n"/>
        <!--
        日誌輸出格式:
            %d{pattern}日期
            %m或者%msg爲信息
            %M爲method
            %L爲行號
            %c類的完整名稱
            %thread線程名稱
            %n換行
            %-5level
        -->
        <!-- 日誌文件存放目錄 -->
        <property name="log_dir" value="d:/logs/"></property>
    
        <!--控制檯輸出appender對象-->
        <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
            <!--輸出流對象 默認 System.out 改成 System.err-->
            <target>System.err</target>
            <!--日誌格式配置-->
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>${pattern}</pattern>
            </encoder>
        </appender>
    
        <!-- 日誌文件拆分和歸檔的appender對象-->
        <appender name="rollFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <!--日誌格式配置-->
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>${pattern}</pattern>
            </encoder>
            <!--日誌輸出路徑-->
            <file>${log_dir}roll_logback.log</file>
            <!--指定日誌文件拆分和壓縮規則-->
            <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
                <!--經過指定壓縮文件名稱,來肯定分割文件方式-->
                <fileNamePattern>${log_dir}rolling.%d{yyyy-MM-dd}.log%i.gz</fileNamePattern>
                <!--文件拆分大小-->
                <maxFileSize>1MB</maxFileSize>
            </rollingPolicy>
            <!--filter配置-->
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <!--設置攔截日誌級別-->
                <level>error</level>
                <onMatch>ACCEPT</onMatch>
                <onMismatch>DENY</onMismatch>
            </filter>
        </appender>
    
        <!--異步日誌-->
        <appender name="async" class="ch.qos.logback.classic.AsyncAppender">
            <appender-ref ref="rollFile"/>
        </appender>
    
        <!--RootLogger對象-->
        <root level="all">
            <appender-ref ref="console"/>
            <appender-ref ref="async"/>
        </root>
    
        <!--自定義logger additivity表示是否從 rootLogger繼承配置-->
        <logger name="com.itheima" level="debug" additivity="false">
            <appender-ref ref="console"/>
        </logger>
    </configuration>
  6. 官方提供的log4j.properties轉換成logback.xml
    https://logback.qos.ch/translator/

3.3 logback-access的使用

logback-access模塊與Servlet容器(如Tomcat和Jetty)集成,以提供HTTP訪問日誌功能。咱們可使用logback-access模塊來替換tomcat的訪問日誌。

  1. 將logback-access.jar與logback-core.jar複製到$TOMCAT_HOME/lib/目錄下
  2. 修改$TOMCAT_HOME/conf/server.xml中的Host元素中添加:

    <Valve className="ch.qos.logback.access.tomcat.LogbackValve" />
  3. logback默認會在$TOMCAT_HOME/conf下查找文件 logback-access.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
        <!-- always a good activate OnConsoleStatusListener -->
        <statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener"/>
        <property name="LOG_DIR" value="${catalina.base}/logs"/>
        <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <file>${LOG_DIR}/access.log</file>
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <fileNamePattern>access.%d{yyyy-MM-dd}.log.zip</fileNamePattern>
            </rollingPolicy>
            <encoder>
            <!-- 訪問日誌的格式 -->
                <pattern>combined</pattern>
            </encoder>
        </appender>
    
        <appender-ref ref="FILE"/>
    </configuration>
  4. 官方配置: https://logback.qos.ch/access.html#configuration

4. log4j2的使用

Apache Log4j 2是對Log4j的升級版,參考了logback的一些優秀的設計,而且修復了一些問題,所以帶來了一些重大的提高,主要有:

  • 異常處理,在logback中,Appender中的異常不會被應用感知到,可是在log4j2中,提供了一些異常處理機制。
  • 性能提高, log4j2相較於log4j 和logback都具備很明顯的性能提高,後面會有官方測試的數據。
  • 自動重載配置,參考了logback的設計,固然會提供自動刷新參數配置,最實用的就是咱們在生產上能夠動態的修改日誌的級別而不須要重啓應用。
  • 無垃圾機制,log4j2在大部分狀況下,均可以使用其設計的一套無垃圾機制,避免頻繁的日誌收集致使的jvm gc。

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

4.1 Log4j2入門

目前市面上最主流的日誌門面就是SLF4J,雖然Log4j2也是日誌門面,由於它的日誌實現功能很是強大,性能優越。因此你們通常仍是將Log4j2看做是日誌的實現,Slf4j + Log4j2應該是將來的大勢所趨。

  1. 添加依賴

    <!-- Log4j2 門面API-->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-api</artifactId>
        <version>2.11.1</version>
    </dependency>
    
    <!-- Log4j2 日誌實現 -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.11.1</version>
    </dependency>
  2. JAVA代碼

    public class Log4j2Test {
        // 定義日誌記錄器對象
        public static final Logger LOGGER = LogManager.getLogger(Log4j2Test.class);
        @Test
        public void testQuick() throws Exception {
            LOGGER.fatal("fatal");
            LOGGER.error("error");
            LOGGER.warn("warn");
            LOGGER.info("info");
            LOGGER.debug("debug");
            LOGGER.trace("trace");
        }
    }
  3. 使用slf4j做爲日誌的門面,使用log4j2做爲日誌的實現

    <!-- Log4j2 門面API-->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-api</artifactId>
        <version>2.11.1</version>
    </dependency>
    
    <!-- Log4j2 日誌實現 -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.11.1</version>
    </dependency>
    
    <!--使用slf4j做爲日誌的門面,使用log4j2來記錄日誌 -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.25</version>
    </dependency>
    
    <!--爲slf4j綁定日誌實現 log4j2的適配器 -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-slf4j-impl</artifactId>
        <version>2.10.0</version>
    </dependency>

4.2 Log4j2配置

log4j2默認加載classpath下的 log4j2.xml 文件中的配置。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" monitorInterval="5">
    <properties>
        <property name="LOG_HOME">D:/logs</property>
    </properties>
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%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>
        
        <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>
        
        <RollingFile name="rollingFile" fileName="${LOG_HOME}/myrollog.log" filePattern="D:/logs/$${date:yyyy-MM-dd}/myrollog-%d{yyyyMM-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} - %msg%n" />
            <Policies>
                <OnStartupTriggeringPolicy />
                <SizeBasedTriggeringPolicy size="10 MB" />
                <TimeBasedTriggeringPolicy />
            </Policies>
            <DefaultRolloverStrategy max="30" />
        </RollingFile>
    </Appenders>
    <Loggers>
        <Root level="trace">
            <AppenderRef ref="Console" />
        </Root>
    </Loggers>
</Configuration>

4.3 Log4j2異步日誌

異步日誌
log4j2最大的特色就是異步日誌,其性能的提高主要也是從異步日誌中受益,咱們來看看如何使用log4j2的異步日誌。

  • 同步日誌
  • 異步日誌

Log4j2提供了兩種實現日誌的方式,一個是經過AsyncAppender,一個是經過AsyncLogger,分別對應前面咱們說的Appender組件和Logger組件。

注意:配置異步日誌須要添加依賴

<!--異步日誌依賴-->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.3.4</version>
</dependency>
  1. AsyncAppender方式

    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration status="warn">
        <properties>
            <property name="LOG_HOME">D:/logs</property>
        </properties>
        <Appenders>
            <File name="file" fileName="${LOG_HOME}/myfile.log">
                <PatternLayout>
                    <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
                </PatternLayout>
            </File>
            <Async name="Async">
                <AppenderRef ref="file"/>
            </Async>
        </Appenders>
        <Loggers>
            <Root level="error">
                <AppenderRef ref="Async"/>
            </Root>
        </Loggers>
    </Configuration>
  2. AsyncLogger方式
    AsyncLogger纔是log4j2 的重頭戲,也是官方推薦的異步方式。它可使得調用Logger.log返回的更快。你能夠有兩種選擇:全局異步和混合異步。
    • 全局異步就是,全部的日誌都異步的記錄,在配置文件上不用作任何改動,只須要添加一個log4j2.component.properties 配置;

      Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
    • 混合異步就是,你能夠在應用中同時使用同步日誌和異步日誌,這使得日誌的配置方式更加靈活。

      <?xml version="1.0" encoding="UTF-8"?>
      <Configuration status="WARN">
          <properties>
              <property name="LOG_HOME">D:/logs</property>
          </properties>
          <Appenders>
              <File name="file" fileName="${LOG_HOME}/myfile.log">
                  <PatternLayout>
                      <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
                  </PatternLayout>
              </File>
              <Async name="Async">
                  <AppenderRef ref="file"/>
              </Async>
          </Appenders>
          <Loggers>
              <AsyncLogger name="com.itheima" level="trace" includeLocation="false" additivity="false">
                  <AppenderRef ref="file"/>
              </AsyncLogger>
              <Root level="info" includeLocation="true">
                  <AppenderRef ref="file"/>
              </Root>
          </Loggers>
      </Configuration>

      如上配置: com.itheima 日誌是異步的,root日誌是同步的。

使用異步日誌須要注意的問題:

  1. 若是使用異步日誌,AsyncAppender、AsyncLogger和全局日誌,不要同時出現。性能會和AsyncAppender一致,降至最低。
  2. 設置includeLocation=false ,打印位置信息會急劇下降異步日誌的性能,比同步日誌還要慢。

4.4 Log4j2的性能

Log4j2最牛的地方在於異步輸出日誌時的性能表現,Log4j2在多線程的環境下吞吐量與Log4j和Logback的比較以下圖。下圖比較中Log4j2有三種模式:
1)全局使用異步模式;
2)部分Logger採用異步模式;
3)異步Appender。能夠看出在前兩種模式下,Log4j2的性能較之Log4j和Logback有很大的優點。

無垃圾記錄
垃圾收集暫停是延遲峯值的常見緣由,而且對於許多系統而言,花費大量精力來控制這些暫停。

許多日誌庫(包括之前版本的Log4j)在穩態日誌記錄期間分配臨時對象,如日誌事件對象,字符串,字符數組,字節數組等。這會對垃圾收集器形成壓力並增長GC暫停發生的頻率。

從版本2.6開始,默認狀況下Log4j以「無垃圾」模式運行,其中重用對象和緩衝區,而且儘量不分配臨時對象。還有一個「低垃圾」模式,它不是徹底無垃圾,但不使用ThreadLocal字段。

Log4j 2.6中的無垃圾日誌記錄部分經過重用ThreadLocal字段中的對象來實現,部分經過在將文本轉換爲字節時重用緩衝區來實現。

使用Log4j 2.5:內存分配速率809 MB /秒,141個無效集合。

Log4j 2.6沒有分配臨時對象:0(零)垃圾回收。

有兩個單獨的系統屬性可用於手動控制Log4j用於避免建立臨時對象的機制:

  • log4j2.enableThreadlocals - 若是「true」(非Web應用程序的默認值)對象存儲在ThreadLocal字段中並從新使用,不然將爲每一個日誌事件建立新對象。
  • log4j2.enableDirectEncoders - 若是將「true」(默認)日誌事件轉換爲文本,則將此文本轉換爲字節而不建立臨時對象。
    注意: 因爲共享緩衝區上的同步,在此模式下多線程應用程序的同步日誌記錄性能可能更差。若是您的應用程序是多線程的而且日誌記錄性能很重要,請考慮使用異步記錄器。

5. SpringBoot中的日誌使用

springboot框架在企業中的使用愈來愈廣泛,springboot日誌也是開發中經常使用的日誌系統。springboot默認就是使用SLF4J做爲日誌門面,logback做爲日誌實現來記錄日誌。

5.1 SpringBoot中的日誌設計

springboot中的日誌

<dependency>
    <artifactId>spring-boot-starter-logging</artifactId>
    <groupId>org.springframework.boot</groupId>
</dependency>

依賴關係圖:

總結:

  1. springboot 底層默認使用logback做爲日誌實現。
  2. 使用了SLF4J做爲日誌門面
  3. 將JUL也轉換成slf4j
  4. 也可使用log4j2做爲日誌門面,可是最終也是經過slf4j調用logback

5.2 SpringBoot日誌使用

  1. 在springboot中測試打印日誌

    @SpringBootTest
    class SpringbootLogApplicationTests {
        //記錄器
        public static final Logger LOGGER = LoggerFactory.getLogger(SpringbootLogApplicationTests.class);
        @Test
        public void contextLoads() {
            // 打印日誌信息
            LOGGER.error("error");
            LOGGER.warn("warn");
            LOGGER.info("info"); // 默認日誌級別
            LOGGER.debug("debug");
            LOGGER.trace("trace");
        }
    }
  2. 修改默認日誌配置

    logging.level.com.itheima=trace
    # 在控制檯輸出的日誌的格式 同logback
    logging.pattern.console=%d{yyyy-MM-dd} [%thread] [%-5level] %logger{50} - %msg%n
    
    # 指定文件中日誌輸出的格式
    logging.file=D:/logs/springboot.log
    logging.pattern.file=%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n
  3. 指定配置
    給類路徑下放上每一個日誌框架本身的配置文件;pringBoot就不使用默認配置的了

    日誌框架 配置文件
    Logback logback-spring.xml , logback.xml
    Log4j2 log4j2-spring.xml , log4j2.xml
    JUL logging.properties

    logback.xml:直接就被日誌框架識別了

  4. 使用SpringBoot解析日誌配置
    logback-spring.xml:由SpringBoot解析日誌配置

    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
        <springProfile name="dev">
            <pattern>${pattern}</pattern>
        </springProfile>
        <springProfile name="pro">
            <pattern>%d{yyyyMMdd:HH:mm:ss.SSS} [%thread] %-5level %msg%n</pattern>
        </springProfile>
    </encoder>

    application.properties

    spring.profiles.active=dev
  5. 將日誌切換爲log4j2

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <exclusions>
        <!--排除logback-->
            <exclusion>
                <artifactId>spring-boot-starter-logging</artifactId>
                <groupId>org.springframework.boot</groupId>
            </exclusion>
        </exclusions>
    </dependency>
    
    <!-- 添加log4j2 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-log4j2</artifactId>
    </dependency>
相關文章
相關標籤/搜索