03.Spring Boot 之日誌體系

1. Java 日誌體系

1.1 市面上常用的日誌框架

日誌門面技術有 JCL(Jakarta Commons Logging)、SLF4j(Simple Logging Facade for Java)、jboss-logging,它們都不提供具體的日誌實現

日誌實現技術有 Log4j、JUL(java.util.logging)、Log4j2、Logback ,它們都提供了不同的 API 使用

Java 日誌體系

1.2 SLF4J 技術

1.2.1 SLF4J 綁定器

每一個日誌的實現框架都有自己的配置文件。使用 SLF4J 以後,配置文件還是做成日誌實現框架自己本身的配置文件

用戶除了需要引入 SLF4J API 的 jar 包外,還需要引入 SLF4J 對日誌實現的綁定包

SLF4J 綁定器

1.2.2 SLF4J 橋接器

SLF4J 橋接器是爲了解決 Jar 包衝突的問題,即有些第三方 Jar 包可能直接使用 Log4j 打印,然後系統中使用的是 SLF4J + Logback 打印,那麼就會出現兩種日誌

使用 SLF4J 橋接器的步驟是:先把其他日誌實現包排除掉,然後使用橋接包來替換,最後導入 SLF4J 綁定器的實現包

SLF4J 橋接器

2. SpringBoot 日誌

2.1 SpringBoot 日誌關係

SpringBoot 底層是使用 SLF4J + Logback 的方式進行日誌記錄,同時引入了 jul-to-slf4jlog4j-to-slf4j 兩個橋接包,從而將 JUL 和 Log4j 的日誌轉到 SLF4J 上,然後再統一使用 Logback 進行日誌輸出

spring-boot-starter-logging 分析

2.2 SpringBoot 日誌使用

代碼已經上傳至 https://github.com/masteryourself-tutorial/tutorial-spring ,詳見 tutorial-spring-boot-core/tutorial-spring-boot-log 工程

2.2.1 配置文件內容
1. application.properties
# 全局日誌配置
logging.level.root=info
# 精確到包名
logging.level.pers.masteryourself.tutorial.spring.boot.log=debug
# 可以指定完整的路徑, 如 D:/springboot.log, 也可以使用 ./ 表示當前路徑
logging.file=./spring.log
# 在當前磁盤的根路徑下創建 spring 文件夾和裏面的 log 文件夾, 使用 spring.log 作爲默認文件, 這兩個配置無法配合使用
logging.path=/spring/log
#  在控制檯輸出的日誌的格式
logging.pattern.console=%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n
# 指定文件中日誌輸出的格式
logging.pattern.file=%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n
2.2.2 配置文件位置

Logback:logback-spring.xml, logback-spring.groovy, logback.xml, logback.groovy

Log4j2: log4j2-spring.xml, log4j2.xml

JUL: logging.properties

如果是原生的 log 配置文件,那麼會直接被日誌框架識別

如果加上了 spring, 例如 logback-spring.xml 或者 log4j2-spring.xml, 那麼日誌框架不會直接加載日誌的配置項, 而是由 SpringBoot 解析,可以使用 SpringBoot 的高級 Profile 功能

<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">

    <layout class="ch.qos.logback.classic.PatternLayout">
    
        <!-- 在 dev 環境下生效 -->
        <springProfile name="dev">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ----> [%thread] ---> %-5level %logger{50} - %msg%n</pattern>
        </springProfile>
        
        <!-- 在非 dev 環境下生效 -->
        <springProfile name="!dev">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ==== [%thread] ==== %-5level %logger{50} - %msg%n</pattern>
        </springProfile>
    </layout>
    
</appender>

2.3 切換日誌框架爲 log4j2

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

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

2.4 日誌模板

1. logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--
    scan:當此屬性設置爲 true 時,配置文件如果發生改變,將會被重新加載,默認值爲 true
    scanPeriod:設置監測配置文件是否有修改的時間間隔,如果沒有給出時間單位,默認單位是毫秒當 scan 爲 true 時, 此屬性生效。默認的時間間隔爲 1 分鐘
    debug:當此屬性設置爲 true 時,將打印出 logback 內部日誌信息,實時查看 logback 運行狀態。默認值爲 false
-->
<configuration scan="false" scanPeriod="60 seconds" debug="false">

    <!-- 定義日誌的根目錄 -->
    <property name="LOG_HOME" value="/app/log"/>
    <!-- 定義日誌文件名稱 -->
    <property name="appName" value="tutorial-spring"/>

    <!-- ch.qos.logback.core.ConsoleAppender 表示控制檯輸出 -->
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <!--
            日誌輸出格式:
			%d 表示日期時間
			%thread 表示線程名
			%-5level:級別從左顯示 5 個字符寬度
			%logger{50} 表示 logger 名字最長 50 個字符,否則按照句點分割
			%msg:日誌消息
			%n 是換行符
        -->
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </layout>
    </appender>

    <!-- 滾動記錄文件,先將日誌記錄到指定文件,當符合某個條件時,將日誌記錄到其他文件 -->
    <appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 指定日誌文件的名稱 -->
        <file>${LOG_HOME}/${appName}.log</file>
        <!--
            當發生滾動時,決定 RollingFileAppender 的行爲,涉及文件移動和重命名
            TimeBasedRollingPolicy: 最常用的滾動策略,它根據時間來制定滾動策略,既負責滾動也負責出發滾動。
        -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--
                滾動時產生的文件的存放位置及文件名稱 %d{yyyy-MM-dd}:按天進行日誌滾動
                %i:當文件大小超過 maxFileSize 時,按照 i 進行文件滾動
            -->
            <fileNamePattern>${LOG_HOME}/${appName}-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
            <!--
                可選節點,控制保留的歸檔文件的最大數量,超出數量就刪除舊文件。假設設置每天滾動
                且 maxHistory 是 365,則只保存最近 365 天的文件,刪除之前的舊文件
            -->
            <MaxHistory>365</MaxHistory>
            <!--
                當日志文件超過 maxFileSize 指定的大小是,根據上面提到的 %i 進行日誌文件滾動
                注意此處配置 SizeBasedTriggeringPolicy 是無法實現按文件大小進行滾動的,必須配置 timeBasedFileNamingAndTriggeringPolicy
            -->
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <!-- 日誌輸出格式: -->
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [ %logger{50} : %line ] - %msg%n</pattern>
        </layout>
    </appender>

    <!--
		logger 主要用於存放日誌對象,也可以定義日誌類型、級別
		name:表示匹配的 logger 類型前綴,也就是包的前半部分
		level:要記錄的日誌級別,包括 TRACE < DEBUG < INFO < WARN < ERROR
		additivity:作用在於 children-logger 是否使用 rootLogger 配置的 appender 進行輸出
		false:表示只用當前 logger 的 appender-ref
		true:表示當前 logger 的 appender-ref 和 rootLogger 的 appender-ref 都有效
    -->

    <!-- tutorial-spring logger -->
    <logger name="pers.masteryourself.tutorial.spring" level="info"/>

    <!-- Spring framework logger -->
    <logger name="org.springframework" level="info" ref="stdout" additivity="false">
        <appender-ref ref="stdout"/>
    </logger>

    <!--
        root 與 logger 是父子關係,沒有特別定義則默認爲 root,任何一個類只會和一個 logger 對應
        要麼是定義的 logger,要麼是 root,判斷的關鍵在於找到這個 logger,然後判斷這個 logger 的 appender 和 level
    -->
    <root level="info">
        <appender-ref ref="stdout"/>
        <appender-ref ref="appLogAppender"/>
    </root>

</configuration>
2. log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- log4j2 自身日誌級別 -->
<Configuration status="WARN">

    <Properties>
        <!-- 定義日誌文件名稱 -->
        <Property name="APP_ID" value="tutorial-spring"/>
        <!-- 定義日誌的根目錄 -->
        <Property name="LOG_PATH" value="/app/log/${APP_ID}"/>
    </Properties>

    <Appenders>
        <RollingFile name="RollingFile" fileName="${LOG_PATH}/app.log"
                     filePattern="${LOG_PATH}/app-%d{yyyyMMdd}-%i.log">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
            <Policies>
                <!--以天爲單位觸發滾動操作,modulate 設置爲 true,會以 00:00 作爲起始時間,每天 00:00 進行一次滾動-->
                <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
                <SizeBasedTriggeringPolicy size="1GB"/>
            </Policies>
            <!--每天最多保存 5G 日誌,最多保存 5 天-->
            <DefaultRolloverStrategy max="5">
                <Delete basePath="${LOG_PATH}" maxDepth="1">
                    <IfFileName glob="app-*.log"/>
                    <IfLastModified age="7d"/>
                </Delete>
            </DefaultRolloverStrategy>
        </RollingFile>

        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </Console>
    </Appenders>

    <Loggers>
        <logger name="pers.masteryourself.tutorial.spring" level="warn"/>

        <root level="INFO">
            <AppenderRef ref="RollingFile"/>
            <AppenderRef ref="Console"/>
        </root>
    </Loggers>

</Configuration>
3. log4j.properties
### set log levels ###
log4j.rootLogger=info ,  stdout ,  D ,  E
### 輸出到控制檯 ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%d{dd/MM/yy HH:mm:ss:SSS z}] %t %5p %c{2}: %m%n
#### 輸出到日誌文件 ###
#log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
#log4j.appender.D.File = logs/log.log
#log4j.appender.D.Append = true
#log4j.appender.D.Threshold = DEBUG ## 輸出DEBUG級別以上的日誌
#log4j.appender.D.layout = org.apache.log4j.PatternLayout
#log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n
#### 保存異常信息到單獨文件 ###
#log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
#log4j.appender.E.File = logs/error.log ## 異常日誌文件名
#log4j.appender.E.Append = true
#log4j.appender.E.Threshold = ERROR ## 只輸出ERROR級別以上的日誌!!!
#log4j.appender.E.layout = org.apache.log4j.PatternLayout
#log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章