1. 程式人生 > >【Spring Boot課程】4.日誌

【Spring Boot課程】4.日誌

1 日誌框架的選擇

1.1 框架一覽

JUL、JCL、JBoss-logging、log4j、log4j2、slf4j等。

日誌門面(抽象層) 日誌實現
JCL(Jakra Commons Logging) SLF4j(Simple Logging Facade for Java) Jboss-Logging Log4j JUL(java.util.logging) Log4j2 Logback

左邊選一個門面(抽象層),右邊選一個實現,但當前最合適的選擇還是:

  • 日誌門面:SLF4J;
  • 日誌實現:Logback

SpringBoot的選擇:其底層由於是Spring框架,Spring框架預設使用的是JCL,所以SB選用的是SLF4Jlogback

2 SLF4j的使用

2.1 使用方法

以後開發的時候,日誌記錄方法的呼叫,不應該直接呼叫日誌的實現類,而是呼叫日誌抽象層裡面的方法。
參考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系統jar演示圖

每一個日誌的實現框架都有自己的配置檔案。使用slf4j以後,配置檔案還是做成日誌實現框架的配置檔案;也就是說,slf4j只是提供了一個抽象標準而已。

2.2 遺留問題

專案A(slf4j+logback):Spring(commons-logging)+Hiberrate(jboss-logging)。
則我們還是得需要統一日誌記錄,即使是別的框架也統一使用slf4j進行輸出。

如何讓系統中的日誌都統一到slf4j:

  1. 將系統中其他日誌框架先排除出去;
  2. 用中間包(如上圖所示)來替換原有的日誌框架;
  3. 匯入slf4j的實現包;

3 SpringBoot日誌原理

3.1 spring-boot中底層依賴

在idea中新建一個專案後,分析pom檔案的依賴,獲得依賴關係圖,留意其中幾個比較核心的依賴部分:

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
      <version>2.1.1.RELEASE</version>
      <scope>compile</scope>
    </dependency>

spring boot中使用他來做日誌功能

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-logging</artifactId>
      <version>2.1.1.RELEASE</version>
      <scope>compile</scope>
    </dependency>

該依賴我們可以看到又依賴了不少日誌框架,從依賴關係中我們可以總結:

  1. spring boot的底層也是使用slf4j + logback的方式記錄日誌;
  2. 剩下還有3個日誌依賴模組,即是上一節用於將其他日誌框架轉為適合slf4j規範的包;
  3. 中間的替換包我們開啟一看,會發現其內部的報名其實和原替換類一模一樣,並且原始碼部分具體邏輯相似,只不過工廠類之類的,用的已經是slf4j的了。
    注意此部分講解的是1.5.10版本的了,2.0以後似乎使用的是另一種設計模式,等以後慢慢研究吧。
  4. 如果我們要引入其他框架,一定要把這個框架的預設日誌依賴移除掉。不然會由於和SB的這些匯入包產生衝突。

注:此部分的內容在2.0.x之後其實已經不一樣了,目前還沒深入研究,之後仔細檢視。

4 Spring boot日誌使用

4.1 簡單示例

@RunWith(SpringRunner.class)
@SpringBootTest
public class Hello3ApplicationTests {
    Logger logger = LoggerFactory.getLogger(getClass());
    @Test
    public void contextLoads() {
        // 日誌的級別
        // 由低到高:trace<debug<info<warn<error
        // 通過此級別關係,我們可以通過調整日誌的級別:日誌就只會在這個級別以及以後的更高級別才生效。
        logger.trace("this is trace log...");
        logger.debug("this is debug log...");
        logger.info("this is info log...");
        logger.warn("this is warn log...");
        logger.error("this is error log...");
    }
}

輸出結果如下:

8-12-11 15:22:49.507  INFO 10116 --- [           main] c.z.s.hello3.Hello3ApplicationTests      : this is info log...
2018-12-11 15:22:49.507  WARN 10116 --- [           main] c.z.s.hello3.Hello3ApplicationTests      : this is warn log...
2018-12-11 15:22:49.507 ERROR 10116 --- [           main] c.z.s.hello3.Hello3ApplicationTests      : this is error log...

從結果我們不難看出,SB的預設日誌級別是info,即只輸出info及其以後更高級別的日誌資訊。

4.2 調整日誌級別

找到配置檔案,新增如下配置:

logging.level.com.zhaoyi = trace

該配置用於將com.zhaoyi包下面的所有日誌記錄級別為trace,這樣就可以保證測試輸出的結果包含trace及其以後級別的輸出了,而其他包則繼續沿用logging.level.root的配置了(info)。

4.3 有關日誌的配置

修改日誌存放方式及路徑

logging.file

如果我們不指定日誌輸出檔案的話,日誌的所有資訊都只會在控制檯輸出,那麼,如果我們想要將日誌內容輸出到具體的日誌檔案該怎麼做呢?
很簡單,使用如下配置:

logging.file=my_log.log

使用該配置之後,spring boot就會將當前生成的配置資訊輸出到當前專案的根路徑下的日誌檔案下,即my_log.log,當然如果您添加了具體的路徑資訊,則就會在指定的路徑下生成日誌檔案,這裡是預設路徑,即當前專案的根目錄下。

logging.path

當然我們也可以配置預設的日誌路徑(注意是路徑,不是檔案)

logging.path=/log_path/

該配置會在當前專案所在的磁碟根目錄生成一個log_path資料夾,並使用spring boot預設的日誌檔名spring.log生成一個日誌檔案。

如果我們同時配置了日誌檔案(logging.file)和日誌路徑(logging.path)兩個選項,那麼生效的是logging.file配置。

修改日誌的輸出格式

logging.pattern.console

該選項用於配置控制檯輸出的日誌格式。如:

logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [ %logger{50} : %line ] - %msg%n

其中:

  • %d表示日期時間;
  • %thread表示執行緒名;
  • %-5level:級別從左顯示5個字元寬度;
  • %logger{50} 表示logger名字最長50個字元,否則按照句點分割;
  • %msg:日誌訊息;
  • %n是換行符;

logging.pattern.file

該配置用於配置日誌檔案記錄的日誌格式,其配置方式同logging.pattern.console

4.4 高階配置

如果我們想要指定自己的相關日誌配置,則需要按如下官方文件所示即可:

Logging System Customization
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

也就是說,我們可以直接在類路徑下放入包含如下配置資訊的日誌配置檔案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">
            <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都有效
    -->
    <!-- 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> 

需要注意的是,在類路徑下放配置檔案的時候,官方也推薦命名帶上spring副檔名,如Logback-spring.xml.其中:

  • logback.xml: 直接就被日誌框架識別了;並且如果配置節中有springProfile的話,logback是無法識別該配置節的,會報錯;
  • logback-spring.xml:日誌框架就不直接載入日誌的配置項,由spring boot的profile模組控制各種不同環境下的輸出。
<springProfile name="dev">
	<!-- configuration to be enabled when the "dev" profile is active -->
</springProfile>

若此時我們指定啟動引數--spring.profiles.active=dev的話,該標籤所包含的配置才會生效。

5 切換日誌框架

前面我們已經知道,sb使用的預設日誌框架為slf4j+logback,倘若我們需要使用其他的日誌框架該如何做呢?(其實不推薦)。可以按照slf4j的日誌是配圖進行相關的切換。例如用slf4j+log4j替換slf4j+logback。在pom檔案中加入如下配置:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
  <exclusions>
    <exclusion>
      <artifactId>logback-classic</artifactId>
      <groupId>ch.qos.logback</groupId>
    </exclusion>
    <exclusion>
      <artifactId>log4j-over-slf4j</artifactId>
      <groupId>org.slf4j</groupId>
    </exclusion>
  </exclusions>
</dependency>

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
</dependency>

又如:我們想使用slf4j+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>

也就是說,在原先的預設pom配置上,排除掉spring-boot-starter-logging,改用spring-boot-starter-log4j2即可。然後我們就在類路徑下寫log4j2的配置檔案就可以了。