天天看點

Spring-boot2使用log4j2中JDBCAppender将日志寫入資料庫(MySql/HikariCP/yml)如何将log4j2內建到Spring-boot結語

Spring-boot2使用log4j2中JDBCAppender将日志寫入資料庫(MySql/HikariCP/yml)

  • 如何将log4j2內建到Spring-boot
    • 1 導入依賴
    • 2 配置log4j2
      • 2.1配置log4j2.xml
      • 2.2 添加log4j2.yml識别依賴
      • 2.3 驗證是否為log4j2輸出
      • 2.4 可以修改日志記錄顔色(僅限Idea)
    • 3 将日志寫入資料庫(MySql+Hikari)
      • 3.1 配置Hikari資料庫連接配接池
      • 3.2 通過JDBCApperder将日志寫入資料庫
        • 1)建立資料庫表
        • 2)初始化連接配接池
        • 3)配置log4j2.yml
  • 結語

如何将log4j2內建到Spring-boot

1 導入依賴

Spring-boot2 中Starters包含log4j2,是以進入log4j2隻要引入以下依賴性進入

pom.xml

<!--日志管理-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
         </dependency>
           

但是,Spring-boot預設是使用Logback來進行日志管理,是以你需要将自帶的log包從Spring-boot中去除。

<!--去掉web中自帶的logging-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
           

如果你在之後遇到了以下 多重綁定錯誤:

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/E:/maven-repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/E:/maven-repository/org/apache/logging/log4j/log4j-slf4j-impl/2.11.2/log4j-slf4j-impl-2.11.2.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]
           

你需要檢查你其他依賴中是否也引入了日志包而造成多重綁定,常見的是

spring-boot-starter-thymeleaf

Zookeeper

兩個依賴包。你可以也将其中的日志包去除掉,這裡以thymeleaf為例:

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
            <!--排除這個預設日志記錄依賴-->
            <exclusions>
            <exclusion>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-logging</artifactId>
            </exclusion>
            </exclusions>
        </dependency>
           

2 配置log4j2

所有配置都按照log4j2官方文檔-配置進行配置。

官方提供了4中配置方式,分别是配置檔案、

ConfigurationFactory

、使用APIs和調用内部日志類方法。

以下是官方說明:

Configuration of Log4j 2 can be accomplished in 1 of 4 ways:

1.Through a configuration file written in XML, JSON, YAML, or properties format.

2.Programmatically, by creating a ConfigurationFactory and Configuration implementation.

3.Programmatically, by calling the APIs exposed in the Configuration interface to add components to the default configuration.

4.Programmatically, by calling methods on the internal Logger class.

2.1配置log4j2.xml

本文采用YAML格式配置,其餘方式可以自行去官方文檔檢視。我這裡采用YAML配置的原因是,這次Spring-boot的配置檔案采用YAML進行配置,覺得格式清晰,操作友善。

我們在

application.yml

或者

application.properties

同級目錄下,建立配置檔案

log4j2.yml

,代碼如下:

Configuration:
  status: warn

  Properties: # 定義全局變量
    Property: # 預設配置(用于開發環境)。其他環境需要在VM參數中指定,如下:
      #測試:-Dlog.level.console=warn -Dlog.level.sdk=trace
      #生産:-Dlog.level.console=warn -Dlog.level.sdk=info
      - name: log.level.console
        value: trace
      - name: log.level.sdk.mapper
        value: debug
      - name: log.path
        value: E:/logs
      - name: project.name
        value: sdk_manage

  Appenders:
    Console:  #輸出到控制台
      name: CONSOLE
      target: SYSTEM_OUT
      ThresholdFilter:
        level: ${sys:log.level.console} # “sys:”表示:如果VM參數中沒指定這個變量值,則使用本檔案中定義的預設全局變量值
        onMatch: ACCEPT
        onMismatch: DENY
      PatternLayout:
        pattern: "%d{yyyy-MM-dd HH:mm:ss,SSS}:%4p %t (%F:%L) - %m%n"
    RollingFile: # 輸出到檔案,超過128MB歸檔
      - name: ROLLING_FILE  #此處添加 “-” 是因為可為多個包配置多個級别,這個數組隻有一個資料,你不添加也可以。
        ignoreExceptions: false
        fileName: ${log.path}/${project.name}.log
        filePattern: "${log.path}/$${date:yyyy-MM}/${project.name}-%d{yyyy-MM-dd}-%i.log.gz"
        PatternLayout:
          pattern: "%d{yyyy-MM-dd HH:mm:ss,SSS}:%4p %t (%F:%L) - %m%n"
        Policies:
          SizeBasedTriggeringPolicy:
            size: "128 MB"
        DefaultRolloverStrategy:
          max: 1000

  Loggers:
    Root:
      level: info
      AppenderRef:
        - ref: CONSOLE
        - ref: ROLLING_FILE
    Logger: # 為com.sdk.management包配置特殊的Log級别,友善調試
      - name: com.sdk.management 
        additivity: false
        level: ${sys:log.level.sdk.mapper}
        AppenderRef:
          - ref: CONSOLE
          - ref: ROLLING_FILE
           

注意:數組類型需要使用

-

進行辨別

其中,

Property

中存放的是後面使用的變量,

Appenders

是配置log4j2 LogEvents存放路徑的,包括

Console

輸出到控制台、

RollingFile

輸出到檔案和

JDBC

輸出到資料庫,你可以根據自己需求取舍。

Loggers

中存放關于日志輸出控制,可以為Appender設定不同的輸出級别,這裡設定info級别日志輸出到控制台和檔案,為sdk.management包單獨設定Log級别。

2.2 添加log4j2.yml識别依賴

此時,

log4j2.yml

檔案是不可以識别的,是因為沒有添加額外依賴,而不是在配置檔案中少了路徑:

# logging設定   
logging:
  config: classpath:log4j2.yml
           

上面這個不用加在配置檔案中,你加上當然也不會報錯。

對此官方的描述如下:

Some Log4J features depend on external libraries.

Feature:Configure YAML Layout

Requirements:Jackson core, databind and YAML data format

其中前兩個spring-boot自帶不用添加,是以需要在

pom.xml

中添加

yaml-data-format

依賴項:

<dependency>	<!-- 加上這個才能辨認到log4j2.yml檔案 -->
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-yaml</artifactId>
        </dependency>
           

到這裡,就已經可以使用log4j2進行日志輸出了。

Spring-boot2使用log4j2中JDBCAppender将日志寫入資料庫(MySql/HikariCP/yml)如何将log4j2內建到Spring-boot結語

2.3 驗證是否為log4j2輸出

如果你用的是Idea的話,本來的日志輸出是有顔色的,log4j2會變的全是白色,同樣,你也可以在配置檔案中添加字段來驗證是否為Log4j2.

Console:  #輸出到控制台
      name: CONSOLE
      target: SYSTEM_OUT
      ThresholdFilter:
        level: ${sys:log.level.console} # “sys:”表示:如果VM參數中沒指定這個變量值,則使用本檔案中定義的預設全局變量值
        onMatch: ACCEPT
        onMismatch: DENY
      PatternLayout:
        pattern: "%d{yyyy-MM-dd HH:mm:ss,SSS}:%4p %t (%F:%L) - %m%n log4j2"   # 你可以在這裡添加log4j2字段,然後測試
           

添加後,輸出結果如下:

Spring-boot2使用log4j2中JDBCAppender将日志寫入資料庫(MySql/HikariCP/yml)如何将log4j2內建到Spring-boot結語

ok,沒有問題。然後我們可以改回來了。

2.4 可以修改日志記錄顔色(僅限Idea)

如果你覺得沒有顔色不夠清晰分别不同級别日志,可以在idea插件中搜尋

Grep Console

進行設定控制台日志輸出顔色。

安裝完插件,重新開機後,進入Settings進行設定,設定如下:

Spring-boot2使用log4j2中JDBCAppender将日志寫入資料庫(MySql/HikariCP/yml)如何将log4j2內建到Spring-boot結語

然後我們可以添加測試類,來看具體輸出情況:

@Test
    public void loggerTest(){
        log.error("ERROR測試");
        log.warn("WARN測試");
        log.info("INFO測試");
        log.debug("DEBUG測試");

    }
           

輸出如下:

Spring-boot2使用log4j2中JDBCAppender将日志寫入資料庫(MySql/HikariCP/yml)如何将log4j2內建到Spring-boot結語

3 将日志寫入資料庫(MySql+Hikari)

log4j2 要求将日志寫入資料庫需要通過連接配接池操作,不然效率太低,是以我們首先要去配置連接配接池,這裡用的是Spring-boot預設的HikariCP,你也可以使用其他的比如Driud等。

首先,我們來配置資料庫連接配接池:

3.1 配置Hikari資料庫連接配接池

如果你在Spring-boot中,并且已經添加了

spring-boot-starter-jdbc

或者

spring-boot-starter-data-jpa

‘starters’ 将會自動将Hikari依賴包導入。

我們隻需要設定資料源就可以了

spring.datasource.type=com.zaxxer.hikari.HikariDataSource
           

具體的yml配置如下:

# 主資料源,預設的
spring:

  # 資料庫通路配置
  datasource:
    name: mydb
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/xxxxx?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
    username: xxxxxxx
    password: xxxxxxx

    # Hikari will use the above plus the following to setup connection pooling
    type: com.zaxxer.hikari.HikariDataSource
    hikari:
      minimum-idle: 5
      maximum-pool-size: 15
      idle-timeout: 30000
      pool-name: DatebookHikariCP
      # 此屬性控制從池傳回的連接配接的預設自動送出行為,預設值:true
      auto-commit: true
      # 此屬性控制池中連接配接的最長生命周期,值0表示無限生命周期,預設1800000即30分鐘
      max-lifetime: 1800000
      # 資料庫連接配接逾時時間,預設30秒,即30000
      connection-timeout: 30000
      connection-test-query: SELECT 1
           

配置完畢,就可以使用Hikari作為你的資料庫連接配接池了。

啟動項目,日志如下:

Spring-boot2使用log4j2中JDBCAppender将日志寫入資料庫(MySql/HikariCP/yml)如何将log4j2內建到Spring-boot結語

3.2 通過JDBCApperder将日志寫入資料庫

這裡需要進行三步操作:

  1. 建立資料庫表
  2. 初始化資料庫池連接配接類,設定方法需傳回類型為Connection
  3. 配置log4j2.yml檔案中Appender->JDBCAppender

1)建立資料庫表

建立資料庫用以存放日志檔案:

-- auto-generated definition
create table log
(
    id            int auto_increment
        primary key,
    sdkName       varchar(64) null,
    sdkUploadTime datetime    null
);
           

2)初始化連接配接池

在config路徑下建立新的類

ConnectionFactoryConfig

,初始化

Hikari

的資料庫連接配接池,傳回一個

Connection

.

此處可以參照HikariCP官方文檔。

實作代碼如下:

package com.sdk.management.config;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

import java.sql.Connection;
import java.sql.SQLException;

/**
 * @author caohongyun
 */
public class ConnectionFactoryConfig{

    private static interface Singleton{
       final ConnectionFactoryConfig INSTANCE = new ConnectionFactoryConfig();
    }

    private HikariDataSource dataSource;

    private ConnectionFactoryConfig(){

        //也可以使用配置檔案直接加載
//        HikariConfig config = new HikariConfig("application.properties");
//        this.dataSource = new HikariDataSource(config);

        String user = "xxxxx";
        String password = "xxxxxx";
        String url = "jdbc:mysql://127.0.0.1:3306/xxxxxx?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai";
        String driverClassName = "com.mysql.cj.jdbc.Driver";

        HikariConfig config = new HikariConfig();
        config.setDriverClassName(driverClassName);
        config.setJdbcUrl(url);
        config.setUsername(user);
        config.setPassword(password);
        config.addDataSourceProperty("cachePrepStmts","true");
        config.addDataSourceProperty("prepstmtCacheSize","250");
        config.addDataSourceProperty("prepstmtCacheSqlLimit","2048");
        //設定連接配接逾時為8小時
        config.setConnectionTimeout(8*60*60);
        this.dataSource = new HikariDataSource(config);
    }
    
    public static Connection getDatabaseConnection() throws SQLException{
        return Singleton.INSTANCE.dataSource.getConnection();
    }
}
           

其中,通過将配置set到HikariConfig中,初始化得到資料源,然後通過

getDatabaseConnection

方法傳回一個

Connection

對象。

3)配置log4j2.yml

可以參考log4j2官方文檔-JDBCAppender進行配置,其中隻有基于.properties的配置方方法,沒有.yml的配置方法。

首先是.properties的官方執行個體:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="error">
  <Appenders>
    <JDBC name="databaseAppender" tableName="LOGGING.APPLICATION_LOG">
      <ConnectionFactory class="net.example.db.ConnectionFactory" method="getDatabaseConnection" />
      <Column name="EVENT_ID" literal="LOGGING.APPLICATION_LOG_SEQUENCE.NEXTVAL" />
      <Column name="EVENT_DATE" isEventTimestamp="true" />
      <Column name="LEVEL" pattern="%level" />
      <Column name="LOGGER" pattern="%logger" />
      <Column name="MESSAGE" pattern="%message" />
      <Column name="THROWABLE" pattern="%ex{full}" />
    </JDBC>
  </Appenders>
  <Loggers>
    <Root level="warn">
      <AppenderRef ref="databaseAppender"/>
    </Root>
  </Loggers>
</Configuration>
           

其次是我自己寫的.yml配置方法:

Configuration:
  status: warn

  Properties: # 定義全局變量
    Property: # 預設配置(用于開發環境)。其他環境需要在VM參數中指定,如下:
      #測試:-Dlog.level.console=warn -Dlog.level.sdk=trace
      #生産:-Dlog.level.console=warn -Dlog.level.sdk=info
      - name: log.level.console
        value: trace
      - name: log.level.sdk.mapper
        value: debug
      - name: log.path
        value: E:/logs
      - name: project.name
        value: sdk_manage

  Appenders:
    Console:  #輸出到控制台
      name: CONSOLE
      target: SYSTEM_OUT
      ThresholdFilter:
        level: ${sys:log.level.console} # “sys:”表示:如果VM參數中沒指定這個變量值,則使用本檔案中定義的預設全局變量值
        onMatch: ACCEPT
        onMismatch: DENY
      PatternLayout:
        pattern: "%d{yyyy-MM-dd HH:mm:ss,SSS}:%4p %t (%F:%L) - %m%n"
    RollingFile: # 輸出到檔案,超過128MB歸檔
      - name: ROLLING_FILE
        ignoreExceptions: false
        fileName: ${log.path}/${project.name}.log
        filePattern: "${log.path}/$${date:yyyy-MM}/${project.name}-%d{yyyy-MM-dd}-%i.log.gz"
        PatternLayout:
          pattern: "%d{yyyy-MM-dd HH:mm:ss,SSS}:%4p %t (%F:%L) - %m%n"
        Policies:
          SizeBasedTriggeringPolicy:
            size: "128 MB"
        DefaultRolloverStrategy:
          max: 1000
    JDBC: #輸出到資料庫
      name: DATABASE
      tableName: log
      ConnectionFactory:
        class: com.sdk.management.config.ConnectionFactoryConfig
        method: getDatabaseConnection
      Column:
        - name: sdkName
          pattern: "%X{sdkName}"
        - name: sdkUploadTime
          pattern: "%d{yyyy-MM-dd hh:mm:ss}"


  Loggers:
    Root:
      level: info
      AppenderRef:
        - ref: CONSOLE
        - ref: ROLLING_FILE
    Logger: # 為com.sdk.management包配置特殊的Log級别,友善調試
      name: com.sdk.management
      additivity: false
      level: ${sys:log.level.sdk.mapper}
      AppenderRef:
        - ref: CONSOLE
        - ref: ROLLING_FILE
        - ref: DATABASE
           

其中比較起來有兩處變動,

第一處是:

Appenders

下多了一個

JDBC

輸出到資料庫,其中幾個比較重要的參數是:

參數 描述
name 必須,Appender名稱
tableName 必須,連接配接資料庫中資料表名
ConnectionFactory.class 必須,指定連接配接池擷取資料庫連接配接類名
ConnectionFactory.method 必須,指定連接配接池擷取資料庫連接配接方法名
Column 必須,資料庫存儲字段以及來源

Note:資料庫字段Pattern格式可以參考Pattern layout官方文檔。

第二處:

你需要設定你的日志輸出到資料庫的級别,我使用

- ref: DATABASE

将其設定為單獨為包添加日志寫入資料庫,當然你也可以自定義資料庫寫入日志級别。

這時你就可以寫一個簡單的類去嘗試将日志寫入資料庫了

這裡簡單寫一個測試類,代碼如下:

這時你可以嘗試啟動項目,

@Test
    public void logger2DatabaseTest(){

        String sdkName = "logByLog4j2";
        String  sdkUploadTime = new Timestamp(System.currentTimeMillis()).toString();

        ThreadContext.put("sdkName",sdkName);
        ThreadContext.put("sdkUploadTime",sdkUploadTime);
        log.debug("日志已經寫入資料庫,sdk名稱為:" + sdkName+ ",上傳時間為:"+ sdkUploadTime);
    }
           

然後執行測試類:

Spring-boot2使用log4j2中JDBCAppender将日志寫入資料庫(MySql/HikariCP/yml)如何将log4j2內建到Spring-boot結語

檢視資料庫表(log):

Spring-boot2使用log4j2中JDBCAppender将日志寫入資料庫(MySql/HikariCP/yml)如何将log4j2內建到Spring-boot結語

寫入成功。

結語

通過log4j2将日志寫入資料庫,感覺用到的地方還是比較小的,一般完全可以通過日志檔案存儲這些日志,

也可以使用AOP切面,對所有的操作進行日志記錄,然後自己手動寫入資料庫。

本來想接着寫如何AOP切面程式設計記錄日志,感覺網上資料挺多的,我就不寫了。