Spring-boot2使用log4j2中JDBCAppender将日志写入数据库(MySql/HikariCP/yml)

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.bootgroupId>
            <artifactId>spring-boot-starter-log4j2artifactId>
         dependency>

但是,Spring-boot默认是使用Logback来进行日志管理,所以你需要将自带的log包从Spring-boot中去除。

        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.bootgroupId>
                    <artifactId>spring-boot-starter-loggingartifactId>
                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-thymeleafZookeeper两个依赖包。你可以也将其中的日志包去除掉,这里以thymeleaf为例:

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-thymeleafartifactId>
            
            <exclusions>
            <exclusion>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-loggingartifactId>
            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>	
            <groupId>com.fasterxml.jackson.dataformatgroupId>
            <artifactId>jackson-dataformat-yamlartifactId>
        dependency>

到这里,就已经可以使用log4j2进行日志输出了。
Spring-boot2使用log4j2中JDBCAppender将日志写入数据库(MySql/HikariCP/yml)_第1张图片

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)_第2张图片
ok,没有问题。然后我们可以改回来了。

2.4 可以修改日志记录颜色(仅限Idea)

如果你觉得没有颜色不够清晰分别不同级别日志,可以在idea插件中搜索Grep Console进行设置控制台日志输出颜色。
安装完插件,重启后,进入Settings进行设置,设置如下:
Spring-boot2使用log4j2中JDBCAppender将日志写入数据库(MySql/HikariCP/yml)_第3张图片
然后我们可以添加测试类,来看具体输出情况:

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

    }

输出如下:
在这里插入图片描述

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)_第4张图片

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的官方实例:


<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);
    }

然后执行测试类:
在这里插入图片描述
查看数据库表(log):
在这里插入图片描述
写入成功。

结语

通过log4j2将日志写入数据库,感觉用到的地方还是比较小的,一般完全可以通过日志文件存储这些日志,
也可以使用AOP切面,对所有的操作进行日志记录,然后自己手动写入数据库。
本来想接着写如何AOP切面编程记录日志,感觉网上资料挺多的,我就不写了。

你可能感兴趣的:(JAVA,log4j2)