1. 程式人生 > >springboot的logback.xml配置和日誌記錄

springboot的logback.xml配置和日誌記錄

最近在研究springboot的日誌,所以記錄一下,做一下總結。

幾篇關於日誌的文章:

介紹日誌:https://blog.csdn.net/gwd1154978352/article/details/78344091

     https://www.cnblogs.com/bigdataZJ/p/springboot-log.html

記錄日誌:https://blog.csdn.net/heweimingming/article/details/76423186

 

日誌,通常不會在需求階段作為一個功能單獨提出來,也不會在產品方案中看到它的細節。但是,這絲毫不影響它在任何一個系統中的重要的地位。

為了保證服務的高可用,發現問題一定要即使,解決問題一定要迅速,所以生產環境一旦出現問題,預警系統就會通過郵件、簡訊甚至電話的方式實施多維轟炸模式,確保相關負責人不錯過每一個可能的bug。

預警系統判斷疑似bug大部分源於日誌。比如某個微服務介面由於各種原因導致頻繁調用出錯,此時呼叫端會捕獲這樣的異常並列印ERROR級別的日誌,當該錯誤日誌達到一定次數出現的時候,就會觸發報警。


try {

    呼叫某服務

} catch(Exception e) {

    LOG.error("錯誤資訊", e);

}

所以日誌至關重要,這篇就來介紹下在Spring Boot如何配置日誌。

Spring Boot預設日誌系統

Spring Boot預設使用LogBack日誌系統,如果不需要更改為其他日誌系統如Log4j2等,則無需多餘的配置,LogBack預設將日誌列印到控制檯上。

1、如果要使用LogBack,原則上是需要新增dependency依賴的

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-logging</artifactId>
</dependency>
但是因為新建的Spring Boot專案一般都會引用spring-boot-starter或者spring-boot-starter-web,而這兩個起步依賴中都已經包含了對於spring-boot-starter-logging
的依賴,
所以,無需額外新增依賴,配置logback-spring.xml就可以了。以logback-spring.xml命名,spring會自動識別載入。

 

2、如果需要切換Log4j2,那麼在pom.xml中需要排除springboot自帶的commons‐logging,然後再引入log4j2的依賴:

<!--排除 commons‐logging-->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
   <exclusions>
      <exclusion>
      <groupId>commons‐logging</groupId>
      <artifactId>commons‐logging</artifactId>
      </exclusion>
   </exclusions>
</dependency>

<!--引入log4j2 -->
<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
然後再引入log4j.properties檔案就可以了。

log4j.properties:
### 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

 

日誌級別

日誌級別由低到高: trace < debug < info < warm < error ,設定的級別越低顯示的日誌級別的資訊越多。
例如:如果設定的日誌級別是info,那麼此時,低於info級別的trace,debug日誌不會顯示。



springboot在不對日誌進行任何設定的情況下,預設日誌root級別是INFO,輸出的是INFO級別以上的日誌。

package com.ll;

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 SpringbootCacheApplicationTests {
    
    private static final Logger log = LoggerFactory.getLogger(SpringbootCacheApplicationTests.class);
    
    /**
      *   測試 日誌級別:
     *    級別由低到高: trace < debug < info < warm < error  設定的級別越低顯示的日誌資訊越多。
     *    可以調整輸出的日誌級別,只會顯示高於設定級別的日誌。
     */
    @Test
    public void testLog() {
        log.trace("這是track日誌。。。");
        log.debug("這是debug日誌。。。");
        //spring 預設設定的級別是info級別,沒有指定級別的情況下,會使用spring預設的root級別(顯示的是info級別的資訊)
        log.info("這是info日誌。。。");
        log.warn("這是warm日誌。。。");
        log.error("這是error日誌。。。");
    }     

}

控制檯資訊:

只會輸出INFO級別以上的日誌資訊,開發環境中,要顯示列印sql語句等debug除錯資訊,要對日誌級別進行設定。

 

日誌的組成部分:

  • 時間日期:精確到毫秒
  • 日誌級別:ERROR, WARN, INFO, DEBUG or TRACE
  • 程序ID
  • 分隔符:— 標識實際日誌的開始
  • 執行緒名:方括號括起來(可能會截斷控制檯輸出)
  • Logger名:通常使用原始碼的類名
  • 日誌內容

 

日誌配置:

日誌可以通過兩種方式配置:

一、application.properties或者application.yml檔案配置:

這種方式需要把所有的日誌配置寫在properties或者yml檔案裡面,配置遷移不方便,寫的感覺也有點亂,很繁雜,對log4j2的支援也不好。推薦用第二種,logback的xml配置方便比較好,配置遷移複製貼上,然後改一下里面的配置就好了。

簡單列舉一下:

 

logging.level.* = LEVEL

  logging.level:日誌級別控制字首,*為包名或Logger名

  LEVEL:選項TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF

舉例說明:

logging.level.com.ll=DEBUG  表示com.ll包下所有class以DEBUG級別輸出(包含mapper所在的包,會列印sql語句)

logging.level.root=INFO    表示root日誌以INFO級別輸出

 

logging.path    該屬性用來配置日誌檔案的路徑

logging.file    該屬性用來配置日誌檔名,如果該屬性不配置,預設檔名為spring.log

logging.path=/springboot/log          在當前磁碟的根路徑下建立spring資料夾和裡面的log資料夾;如果不配置,使用 spring.log 作為預設檔案

logging.file=E:/springboot/log/spring.log    可以指定完整的路徑(logging.path和logging.file 配置一個即可)        

 

另外還有日誌的列印位置設定:

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

logging.pattern.file=%d{yyyy/MM/dd-HH:mm} [%thread] %-5level %logger- %msg%n

logging.pattern.console

該屬性用於定製日誌輸出格式。上述配置的編碼中,對應符號的含義如下:

%d{HH:mm:ss.SSS}——日誌輸出時間

%thread——輸出日誌的程序名字,這在Web應用以及非同步任務處理中很有用

%-5level——日誌級別,並且使用5個字元靠左對齊

%logger- ——日誌輸出者的名字

%msg——日誌訊息

%n——平臺的換行符

appliacation.properties:

# com.ll包下所有class以DEBUG級別輸出(包含mapper所在的包,會列印sql語句)
logging.level.com.ll=DEBUG  
# root日誌以INFO級別輸出
logging.level.root=INFO

# 在當前磁碟的根路徑下建立spring資料夾和裡面的log資料夾;如果不配置,使用 spring.log 作為預設檔案
#logging.path=/springboot/log
#
可以指定完整的路徑(logging.path和logging.file 配置一個即可)
logging.file=E:/springboot/log/spring.log     
#控制檯日誌格式
logging.pattern.console=%d{yyyy/MM/dd-HH:mm:ss} [%thread] %-5level %logger- %msg%n
#檔案日誌格式
logging.pattern.file=%d{yyyy/MM/dd-HH:mm} [%thread] %-5level %logger- %msg%n

 

 

二、logback的xml檔案配置:

由於日誌服務一般都在ApplicationContext建立前就初始化了,它並不是必須通過Spring的配置檔案控制。因此通過系統屬性和傳統的Spring Boot外部配置檔案依然可以很好的支援日誌控制和管理。

根據不同的日誌系統,你可以按如下規則組織配置檔名,就能被正確載入:

Logback:logback-spring.xml, logback-spring.groovy, logback.xml, logback.groovy
Log4j:log4j-spring.properties, log4j-spring.xml, log4j.properties, log4j.xml
Log4j2:log4j2-spring.xml, log4j2.xml
JDK (Java Util Logging):logging.properties
Spring Boot官方推薦優先使用帶有-spring的檔名作為你的日誌配置(如使用logback-spring.xml,而不是logback.xml),命名為logback-spring.xml的日誌配置檔案,spring boot可以為它新增一些spring boot特有的配置項(下面會提到)。

上面是預設的命名規則,並且放在src/main/resources下面即可。

 

如果你即想完全掌控日誌配置,但又不想用logback.xml作為Logback配置的名字,可以通過logging.config屬性指定自定義的名字:ogging.config=classpath:logging-config.xm

雖然一般並不需要改變配置檔案的名字,但是如果你想針對不同執行時Profile使用不同的日誌配置,這個功能會很有用。

 

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="ll-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都有效
    -->
    <!--
        logger是記錄Logger物件輸出的日誌級別的
          sercvice實現類引入日誌物件可以檢視方法的報錯資訊以及列印sql語句,public static final Logger logger = LoggerFactory.getLogger(SysUserServiceImpl.class);
      生產環境:
       一般把level設為error,可以記錄錯誤的日誌資訊,畢竟主要是要記錄錯誤資訊進行錯誤定位。
     開發環境:
       類中引入了logger日誌物件時,level級別用info,debug都可以,都有錯誤資訊輸出。
   -->
    <!-- hibernate logger -->
    <logger name="com.ll" level="info" />
    <!-- Spring framework logger -->
    <logger name="org.springframework" level="debug" additivity="false"></logger>



    <!-- 
    root與logger是父子關係,沒有特別定義則預設為root,任何一個類只會和一個logger對應,
    要麼是定義的logger,要麼是root,判斷的關鍵在於找到這個logger,然後判斷這個logger的appender和level。 
    -->
  <!-- 一般用預設的info就可以 -->
    <root level="info">
        <!-- 控制檯輸出日誌-->
        <appender-ref ref="stdout" />
        <!--
        開發環境:
            不需要往檔案記錄日誌,可以把這個appender-ref ref="appLogAppender"註釋,上面那個往檔案寫日誌的appender也要註釋,不然每天都產生一個空檔案;
        生產環境:
            需要往檔案記錄日誌,此時appender-ref ref="appLogAppender"就不能註釋了,不然沒日誌記錄到檔案,上面那個往檔案寫日誌的appender也要放開。
         -->
        <appender-ref ref="appLogAppender" />
    </root>
</configuration> 

 

配置完logback-spring.xml,接下來需要做的就是在把Logger物件引入到需要記錄日誌的類了。

public static final Logger logger = LoggerFactory.getLogger(SysUserServiceImpl.class);

下面列舉一個記錄批量儲存方法記錄錯誤日誌的例子:

 

package com.ll.service.impl;

import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import com.ll.bean.SysUser;
import com.ll.mapper.SysUserMapper;
import com.ll.service.SysUserService;
import com.ll.utils.MyException;


@Service
public class SysUserServiceImpl implements SysUserService {

    public static final Logger logger = LoggerFactory.getLogger(SysUserServiceImpl.class);
    
    @Autowired
    SysUserMapper sysUserMapper;


    //開啟事務
    @Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,rollbackFor = MyException.class)
    @Override
    public int batchInsertUser(List<SysUser> userList) throws MyException {
        //
        try {
            sysUserMapper.batchInsertUser(userList);
        } catch (Exception e) {
       /**推薦printStackTrace()列印堆疊錯誤資訊這個方法保留,紅色錯誤提示,開發的時候更方便檢視錯誤資訊*/ 
            e.printStackTrace();
            /**logger.error方法把異常錯誤e記錄進日誌檔案,開發環境用logger.error方法結合printStackTrace()列印堆疊錯誤資訊的方法就可以了*/
            logger.error("method:batchInsertUser,error:",e);
            /**因為logback-spring.xml的logger的level級別設定為error時,logger.info的訊息不會列印,也不會記錄進日誌檔案,所以不要用logger.info方法,開發環境用printStackTrace()列印堆疊錯誤資訊即可。*/
            //logger.info("method:batchInsertUser",e);
           
            throw new MyException("批量新增失敗!",e.getCause());
        }
        return 1;
    }
    

}                    

 

 

 

 

有時候logger.error不能完全地打印出網站的錯誤堆疊資訊,只能列印這個錯誤是一個什麼錯誤。

 

 

為什麼?

 

看Logger.error原始碼

 

public void error(String msg, Throwable t);

 

public void error(String msg);

 

 

 

如果只傳一個引數e進去,那麼e就被認為是String型別(會自動調toString()方法把Exception轉成String),而不是Exception型別。

 

如果想列印堆疊資訊,那麼必須傳兩個或以上引數,實際上就是為了呼叫public void error(String msg, Throwable t);

 

 

 

所以我們的寫法可以是:

 

Logger.error(“xxx出錯”,e); //第二個引數是e

 

而不是:

 

Logger.error(“xxx出錯:”+e) 或 logger.error(e) 或logger.error(e.getMessage);

到此,springboot的slf4j簡單日誌門面,預設使用的logback-spring.xml配置和日誌記錄就完成了~~~~~~~

&n