1. 程式人生 > >Spring Boot採用yml的方式配置 Log4j2 日誌檔案

Spring Boot採用yml的方式配置 Log4j2 日誌檔案

從圖中不難看出,線上程數為 2~16 之間,混合使用同步和非同步的logger來列印日誌,效能是最好的

1、選擇Log4j2的理由是啥,為啥不用spring boot 預設的?

優化說明
執行速度Log4j 2.x 相對於 Log4j 1.x 和 Logback來說,具有更快的執行速度。一方面由於 重寫了內部的實現,在某些特定的場景上面,甚至可以比之前的速度快上10倍。比如內部的訊息佇列採用了ArrayBlockingQueue,相對於原始的ArrayList和鎖操作來說,管程類的訊息佇列擁有更好地效能。同時所需的記憶體更加少。這是因為Log4j 2.x 採用佔位符的形式列印日誌(類似於Slf4j門面日誌的形式),會先判斷一下日誌的等級,然後再拼接要列印的內容。另一方面由於Log4j 2.x 充分利用Java 5的併發特性(主要是使用了一些concurrent包下鎖),使得效能得到一定的改善,而Log4j 1.x和Logback很多地方還是用的重鎖。
非同步效能Asynchronous Loggers是Log4j2新增的日誌器,非同步日誌器在其內部實現採用了LMAX Disruptor(一個無鎖的執行緒間通訊庫)技術,Disruptor主要通過環形陣列結構、元素位置定位和精巧的無鎖設計(CAS)實現了在高併發情形下的高效能。而且Log4j 2.x中Asynchronous Appenders作為Asynchronous Loggers工作的一部分,效果進行了增強。每次寫入磁碟時,都會進行flush操作,效果和配置“immediateFlush=true”一樣。該非同步Appender內部採用ArrayBlockingQueue的方式。RandomAccessFileAppender採用ByteBuffer+RandomAccessFile替代了BufferedOutputStream,官方給出的測試資料是它將速度提升了20-200%。
自動載入配置檔案Log4j 2.x 和Logback都新增了自動載入日誌配置檔案的功能,又與Logback不同,配置發生改變時不會丟失任何日誌事件。當Log4j 2.x中配置發生改變時,如果還有日誌事件尚未處理,Log4j 2會繼續處理,當處理完成後,Logger會重新指向新配置的LoggerConfig物件,並且刪除無用的物件。
死鎖問題的解決在Log4j 1.x中同步寫日誌的時候,在高併發情況下出現死鎖導致cpu使用率異常飆升。其中的原因是當一個程序寫日誌的時候需要獲取到Logger和Appender。org.apache.log4j.Logger類繼承於org.apache.log4j.Category、Appender繼承於org.apache.log4j.AppenderSkeleton。通過Log4j 1.x中Category原始碼和Appender原始碼可以知道,當多執行緒併發時,可能會因為相互持有Logger和Appender發生死鎖。 而在log4j 2.x中充分利用Java5的併發支援,並且以最低級別執行鎖定。

2、Maven 依賴 pom.xml配置

去掉預設日誌,載入別的日誌,spring boot提供log4j2的解決方案,如下配置
                <!-- 包含 mvc,aop 等jar資源 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
			<exclusions>
			<!-- 切換log4j2日誌讀取 -->
				<exclusion>
					<groupId>org.springframework.boot</groupId>
					<artifactId>spring-boot-starter-logging</artifactId>
				</exclusion>
			</exclusions>
		</dependency>

然後新增如下兩個依賴

                <!-- 配置 log4j2 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-log4j2</artifactId>
		</dependency>
		<!-- 加上這個才能辨認到log4j2.yml檔案 -->
		<dependency>
			<groupId>com.fasterxml.jackson.dataformat</groupId>
			<artifactId>jackson-dataformat-yaml</artifactId>
		</dependency>

3、配置檔案新增log4j2.yml

檔案存放resource目錄下
# 共有8個級別,按照從低到高為:ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF。

Configuration:
  status: warn
  monitorInterval: 30

  Properties: # 定義全域性變數
    Property: # 預設配置(用於開發環境)。其他環境需要在VM引數中指定,如下:
      #測試:-Dlog.level.console=warn -Dlog.level.xjj=trace
      #生產:-Dlog.level.console=warn -Dlog.level.xjj=info
      - name: log.level.console
        value: info
      - name: log.path
        value: log
      - name: project.name
        value: opendoc
      - name: log.pattern
        value: "%d{yyyy-MM-dd HH:mm:ss.SSS} -%5p ${PID:-} [%15.15t] %-30.30C{1.} : %m%n"

  Appenders:
    Console:  #輸出到控制檯
      name: CONSOLE
      target: SYSTEM_OUT
      PatternLayout:
        pattern: ${log.pattern}
#   啟動日誌
    RollingFile:
      - name: ROLLING_FILE
        fileName: ${log.path}/${project.name}.log
        filePattern: "${log.path}/historyRunLog/$${date:yyyy-MM}/${project.name}-%d{yyyy-MM-dd}-%i.log.gz"
        PatternLayout:
          pattern: ${log.pattern}
        Filters:
#        一定要先去除不接受的日誌級別,然後獲取需要接受的日誌級別
          ThresholdFilter:
            - level: error
              onMatch: DENY
              onMismatch: NEUTRAL
            - level: info
              onMatch: ACCEPT
              onMismatch: DENY
        Policies:
          TimeBasedTriggeringPolicy:  # 按天分類
            modulate: true
            interval: 1
        DefaultRolloverStrategy:     # 檔案最多100個
          max: 100
#   平臺日誌
      - name: PLATFORM_ROLLING_FILE
        ignoreExceptions: false
        fileName: ${log.path}/platform/${project.name}_platform.log
        filePattern: "${log.path}/platform/$${date:yyyy-MM}/${project.name}-%d{yyyy-MM-dd}-%i.log.gz"
        PatternLayout:
          pattern: ${log.pattern}
        Policies:
          TimeBasedTriggeringPolicy:  # 按天分類
            modulate: true
            interval: 1
        DefaultRolloverStrategy:     # 檔案最多100個
          max: 100
#   業務日誌
      - name: BUSSINESS_ROLLING_FILE
        ignoreExceptions: false
        fileName: ${log.path}/bussiness/${project.name}_bussiness.log
        filePattern: "${log.path}/bussiness/$${date:yyyy-MM}/${project.name}-%d{yyyy-MM-dd}-%i.log.gz"
        PatternLayout:
          pattern: ${log.pattern}
        Policies:
          TimeBasedTriggeringPolicy:  # 按天分類
            modulate: true
            interval: 1
        DefaultRolloverStrategy:     # 檔案最多100個
          max: 100
#   錯誤日誌
      - name: EXCEPTION_ROLLING_FILE
        ignoreExceptions: false
        fileName: ${log.path}/exception/${project.name}_exception.log
        filePattern: "${log.path}/exception/$${date:yyyy-MM}/${project.name}-%d{yyyy-MM-dd}-%i.log.gz"
        ThresholdFilter:
          level: error
          onMatch: ACCEPT
          onMismatch: DENY
        PatternLayout:
          pattern: ${log.pattern}
        Policies:
          TimeBasedTriggeringPolicy:  # 按天分類
            modulate: true
            interval: 1
        DefaultRolloverStrategy:     # 檔案最多100個
          max: 100
#   DB 日誌
      - name: DB_ROLLING_FILE
        ignoreExceptions: false
        fileName: ${log.path}/db/${project.name}_db.log
        filePattern: "${log.path}/db/$${date:yyyy-MM}/${project.name}-%d{yyyy-MM-dd}-%i.log.gz"
        PatternLayout:
          pattern: ${log.pattern}
        Policies:
          TimeBasedTriggeringPolicy:  # 按天分類
            modulate: true
            interval: 1
        DefaultRolloverStrategy:     # 檔案最多100個
          max: 100


  Loggers:
    Root:
      level: info
      AppenderRef:
        - ref: CONSOLE
        - ref: ROLLING_FILE
        - ref: EXCEPTION_ROLLING_FILE
    Logger:
      - name: platform
        level: info
        additivity: false
        AppenderRef:
          - ref: CONSOLE
          - ref: PLATFORM_ROLLING_FILE

      - name: bussiness
        level: info
        additivity: false
        AppenderRef:
          - ref: BUSSINESS_ROLLING_FILE

      - name: exception
        level: debug
        additivity: true
        AppenderRef:
          - ref: EXCEPTION_ROLLING_FILE

      - name: db
        level: info
        additivity: false
        AppenderRef:
          - ref: DB_ROLLING_FILE


#    監聽具體包下面的日誌
#    Logger: # 為com.xjj包配置特殊的Log級別,方便除錯
#      - name: com.xjj
#        additivity: false
#        level: ${sys:log.level.xjj}
#        AppenderRef:
#          - ref: CONSOLE
#          - ref: ROLLING_FILE

4、引入配置檔案

在application.yml引入
logging:
  config: classpath:log4j2.yml 


5、不同日誌工具類util編輯

由於配置了4個檔案存放不同日誌,分別為平臺日誌(${project.name}_platform.log)、 業務日誌(${project.name}_bussiness.log)、錯誤日誌(${project.name}_exception.log)、DB 日誌(${project.name}_db.log),資料夾外面為執行日誌,不同檔案日誌級別不一樣,因此程式設計師在開發時候需要注意引入不同日誌,也就是說開發出現的日誌,程式設計師可以進行分類,分別呼叫公共方法即可。公共類編輯如下;

package com.open.util;
/**
 * 本地日誌列舉
 * @author Administrator
 *
 */
public enum  LogEnum {


    BUSSINESS("bussiness"),

    PLATFORM("platform"),

    DB("db"),

    EXCEPTION("exception"),

    ;


    private String category;


    LogEnum(String category) {
        this.category = category;
    }

    public String getCategory() {
        return category;
    }

    public void setCategory(String category) {
        this.category = category;
    }
}
package com.open.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * 本地日誌參考類
 * @author Administrator
 *
 */
public class LogUtils {


    /**
     * 獲取業務日誌logger
     *
     * @return
     */
    public static Logger getBussinessLogger() {
        return LoggerFactory.getLogger(LogEnum.BUSSINESS.getCategory());
    }

    /**
     * 獲取平臺日誌logger
     *
     * @return
     */
    public static Logger getPlatformLogger() {
        return LoggerFactory.getLogger(LogEnum.PLATFORM.getCategory());
    }

    /**
     * 獲取資料庫日誌logger
     *
     * @return
     */
    public static Logger getDBLogger() {
        return LoggerFactory.getLogger(LogEnum.DB.getCategory());
    }


    /**
     * 獲取異常日誌logger
     *
     * @return
     */
    public static Logger getExceptionLogger() {
        return LoggerFactory.getLogger(LogEnum.EXCEPTION.getCategory());
    }


}

具體呼叫如下:

 @GetMapping("/helloworld")
    public String helloworld() throws Exception{
    	Logger log = LogUtils.getExceptionLogger();
    	Logger log1 = LogUtils.getBussinessLogger();
    	Logger log2 = LogUtils.getDBLogger();
    	userService.queryUser();
    	log.error("getExceptionLogger===日誌測試");
    	log1.info("getBussinessLogger===日誌測試");
    	log2.debug("getDBLogger===日誌測試");
        return "helloworld";
    }

生成日誌目錄如下