Spring Boot--日誌框架的學習

薛定諤的Carson發表於2019-07-02

Spring Boot--日誌框架的學習

版本:

  • Spring Boot 2.1.5.RELEASE

  • maven 3.2.5

  • jdk1.8

1.我選用的日誌框架:

  • 日誌門面(抽象層): SLF4J
  • 日誌實現: Logback

Spring Boot: 底層是Spring框架,Spring框架預設是JCL;

而Spring Boot選用 SLF4j和Logback


2. SLF4j使用

1) 如何在系統中使用SLF4j

SLF4j的官方手冊

開發的時候,日誌記錄方法的呼叫,不用改直接呼叫日誌的實現類,而是呼叫日誌抽象層裡的方法;

  • 匯入slf4j的jar和 logback的實現jar
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HelloWorld {
  public static void main(String[] args) {
    Logger logger = LoggerFactory.getLogger(HelloWorld.class);
    logger.info("Hello World");
  }
}
複製程式碼

SLF4j與其他日誌框架結合示意圖:

Spring Boot--日誌框架的學習

2) 遺留問題

雖然我使用的是(slf4j & logback),但是其他的比如Spring框架用的commons-logging日誌框架, Hibernate用的是jboss-logging日誌框架,等等,太亂了了,所以我們要 統一日誌記錄,即使別的框架也要和我一起統一使用slf4j進行輸出;

官方給出的示意圖:

Spring Boot--日誌框架的學習

邏輯:

  • 1.將系統其他的日誌框架排除;
  • 2.用中間包來替換原有的日誌框架(紅色框起來的就是中間包);
  • 3.匯入slf4j其他的實現

3. Spring Boot日誌關係

其實是這個依賴的這個包:

	<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-logging</artifactId>
      <version>2.1.5.RELEASE</version>
      <scope>compile</scope>
    </dependency>
複製程式碼

pom檔案下滑鼠右鍵點選:

Spring Boot--日誌框架的學習

SpringBoot所有依賴關係圖,我們只看logging相關的:

Spring Boot--日誌框架的學習

我用的是2.1.5版本的Spring Boot, 筆者在學習過程中發現我看的老師視訊裡跟我的版本不一樣,

這是那位老師的版本:

Spring Boot--日誌框架的學習

很明顯跟我不一樣,不過Spring肯定對自己的底層進行了升級;

確切的說不是升級,而是剔除掉了jcl框架的轉換,因為jcl框架最後更新日期是2014年,明顯已經過時

但是基本推理的出來,slf4j的官網圖確實是對SpringBoot1.x版本的示意,但是對我的2.x版本不符, 不過根據我的包名來看,他們的功能是差不多的,都是轉成slf4j;

小總結:

  • 1.SpringBoot底層也是 slf4j+logback的方式j進行日誌記錄;
  • 2.SpringBoot也把其他日誌都替換成了slf4j;
  • 3.中間替換包;

中間替換包圖示:

Spring Boot--日誌框架的學習

4) 如果我們要引入其他的框架,一定要把這個框架預設日誌依賴移除掉!

Spring框架預設的日誌框架是: commons-logging;

但是Spring Boot引入spring核心jar包的時候去除了 日誌jar包;

Spring Boot能自動適配所有的日誌,而且底層使用 slf4j+logback的方式記錄日誌,引入其他框架的時候,只需要把這個框架依賴的日誌排除掉


4. 日誌使用

測試類:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBootLoggingApplicationTests {
    // 日誌記錄器記錄器
    Logger logger = LoggerFactory.getLogger(getClass());
    @Test
    public void contextLoads() {
        // 日誌的級別
        // 由低到高 trace < debug < info < warn < error

        logger.trace("這是trace日誌..");
        logger.debug("這是debug除錯日誌..");
        logger.info("這是info日誌..");
        logger.warn("這是warn日誌...");
        logger.error("這是error錯誤日誌");
    }

}
複製程式碼

執行輸出結果:

2019-07-02 19:22:35.980  INFO 8404 --- [           main] c.c.s.SpringBootLoggingApplicationTests  : 這是info日誌..
2019-07-02 19:22:35.981  WARN 8404 --- [           main] c.c.s.SpringBootLoggingApplicationTests  : 這是warn日誌...
2019-07-02 19:22:35.981 ERROR 8404 --- [           main] c.c.s.SpringBootLoggingApplicationTests  : 這是error錯誤日誌
複製程式碼

也就是說 SpringBoot預設的日誌級別是 info,所以只輸出了info 和 比info級別大的日誌;

開啟我的yml配置檔案:

logging:
  level:
    com.carson: trace
複製程式碼

意思是把我 com.carson下的所有包都設定為 trace 日誌級別

輸出結果:

2019-07-02 19:29:17.406 TRACE 7076 --- [           main] c.c.s.SpringBootLoggingApplicationTests  : 這是trace日誌..
2019-07-02 19:29:17.407 DEBUG 7076 --- [           main] c.c.s.SpringBootLoggingApplicationTests  : 這是debug除錯日誌..
2019-07-02 19:29:17.407  INFO 7076 --- [           main] c.c.s.SpringBootLoggingApplicationTests  : 這是info日誌..
2019-07-02 19:29:17.408  WARN 7076 --- [           main] c.c.s.SpringBootLoggingApplicationTests  : 這是warn日誌...
2019-07-02 19:29:17.408 ERROR 7076 --- [           main] c.c.s.SpringBootLoggingApplicationTests  : 這是error錯誤日誌
複製程式碼

4.1 配置檔案詳解:

logging.level.com.carson= trace
# path: 只建立任意一個資料夾, SpringBoot會預設生成一個 spring.log 作為日誌檔案
logging.path= F:/spring/log
# file: 指定一個路徑和檔案把 日誌輸出到 指定檔案裡
logging.file= F:/springboot.log
# pattern.console: 控制檯輸出的日誌格式
logging.pattern.console= %d{yyyy‐MM‐dd} === [%thread] === %‐5level === %logger{50} ==== %msg%n
#   pattern.file : 指定檔案中日誌的輸出格式
logging.pattern.file= %d{yyyy‐MM‐dd} === [%thread] === %‐5level === %logger{50} ==== %msg%n
複製程式碼

4.2 指定配置

日誌框架 檔案命名
Logback logback-spring.xml, logback-spring.groovy, logback.xml, or logback.groovy
Log4j2 log4j2-spring.xml or log4j2.xml
JDK (Java Util Logging) logging.properties

以上來自SpringBoot的官方文件, 上面是他要求的命名,SpringBoot會讀取的

  • logback-spring.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="atguigu-springboot"></property>
    <!-- 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">
            <springProfile name="dev">
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ----> [%thread] ---> %-5level %logger{50} - %msg%n</pattern>
            </springProfile>
            <springProfile name="!dev">
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ==== [%thread] ==== %-5level %logger{50} - %msg%n</pattern>
            </springProfile>
        </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都有效
    -->
    <!-- hibernate logger -->
    <logger name="com.atguigu" level="debug" />
    <!-- Spring framework logger -->
    <logger name="org.springframework" level="debug" additivity="false"></logger>



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

把上面配置複製一份,放到專案的 resources 裡面,或者自定義配置,命名規範的區別:

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

  • 但是如果你以 logback-spring.xml 命名(推薦): 日誌框架不直接載入日誌的配置項了,由SpringBoot解析日誌你就可以使用SpringBoot的一個高階功能

<springProfile name="prod">
	<!-- 是不是 prod 環境 -->
</springProfile>

<springProfile name="dev | prod">
	<!--  dev 和 prod 都會執行 -->
</springProfile>

<springProfile name="!dev">
	<!-- 如果不是 dev環境 -->
</springProfile>
複製程式碼

也就是剛才的配置檔案裡的:

<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">
            <springProfile name="dev">
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ----> [%thread] ---> %-5level %logger{50} - %msg%n</pattern>
            </springProfile>
            <springProfile name="!dev">
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ==== [%thread] ==== %-5level %logger{50} - %msg%n</pattern>
            </springProfile>
        </layout>
    </appender>
複製程式碼

你可以在你的 yml或者properties裡配置一條:

spring.profiles.active= prod
複製程式碼

來設定執行環境

4.3 使用 log4j

其實SpringBoot官方為我們做了一些幫助:

Name Description Pom
spring-boot-starter-jetty Starter for using Jetty as the embedded servlet container. An alternative to spring-boot-starter-tomcat Pom
spring-boot-starter-log4j2 Starter for using Log4j2 for logging. An alternative to spring-boot-starter-logging Pom
spring-boot-starter-logging Starter for logging using Logback. Default logging starter Pom
spring-boot-starter-reactor-netty Starter for using Reactor Netty as the embedded reactive HTTP server. Pom
spring-boot-starter-tomcat Starter for using Tomcat as the embedded servlet container. Default servlet container starter used by spring-boot-starter-web Pom
spring-boot-starter-undertow Starter for using Undertow as the embedded servlet container. An alternative to spring-boot-starter-tomcat Pom

想使用哪個日誌框架就直接匯入哪個包就好了, 比如我想使用 log4j2 我先把預設的 spring-boot-starter-logging 排除掉 , 排除方法如下:

Spring Boot--日誌框架的學習

  • 然後再pom檔案裡:
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
            <version>2.1.1.RELEASE</version>
        </dependency>
複製程式碼

匯入log4j2的包;

  • 然後在 resources 裡 建立一個 log4j的配置檔案,檔案命名就以剛才說的:

內容:

### set log levels ###
log4j.rootLogger = debug ,  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{ABSOLUTE} ===== %5p %c{ 1 }:%L - %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.D = org.apache.log4j.DailyRollingFileAppender
#log4j.appender.D.File = logs/error.log ## 異常日誌檔名
#log4j.appender.D.Append = true
#log4j.appender.D.Threshold = ERROR ## 只輸出ERROR級別以上的日誌!!!
#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
複製程式碼

也就是說,如果像引入其他日誌框架,只要把以前的框架排除,然後再引入新框架就ok了


個人部落格 : aaatao66.github.io/

相關文章