1. 程式人生 > 程式設計 >SpringBoot Logback日誌記錄到資料庫的實現方法

SpringBoot Logback日誌記錄到資料庫的實現方法

對於日誌的處理,有時候需要把符合條件的日誌計入資料庫中

一、新增pom依賴

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- 這個依賴必須存在,否則會報java.lang.ClassNotFoundException: org.apache.commons.dbcp.BasicDataSource-->
    <dependency>
      <groupId>commons-dbcp</groupId>
      <artifactId>commons-dbcp</artifactId>
      <version>1.4</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <scope>runtime</scope>
    </dependency>

二、建立logback配置檔案

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
  <!--定義日誌檔案的儲存地址 勿在 LogBack 的配置中使用相對路徑-->
  <property name="LOG_HOME" value="/home/admin" />

  <!-- 控制檯輸出 -->
  <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
      <!--格式化輸出:%d表示日期,%thread表示執行緒名,%-5level:級別從左顯示5個字元寬度%msg:日誌訊息,%n是換行符-->
      <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
    </encoder>
  </appender>

  <!-- 按照每天生成日誌檔案 -->
  <appender name="application_file" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <!--日誌檔案輸出的檔名-->
      <FileNamePattern>${LOG_HOME}/info/info.log.%d{yyyy-MM-dd}.log</FileNamePattern>
      <!--日誌檔案保留天數-->
      <MaxHistory>30</MaxHistory>
    </rollingPolicy>
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
      <!--格式化輸出:%d表示日期,%thread表示執行緒名,%-5level:級別從左顯示5個字元寬度%msg:日誌訊息,%n是換行符-->
      <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
    </encoder>
    <!--日誌檔案最大的大小-->
    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
      <MaxFileSize>500MB</MaxFileSize>
    </triggeringPolicy>
  </appender>

  <!-- 異常日誌檔案 -->
  <appender name="error_file" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <!--日誌檔案輸出的檔名-->
      <FileNamePattern>${LOG_HOME}/error/error.log.%d{yyyy-MM-dd}.log</FileNamePattern>
      <!--日誌檔案保留天數-->
      <MaxHistory>30</MaxHistory>
    </rollingPolicy>
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
      <!--格式化輸出:%d表示日期,%thread表示執行緒名,%-5level:級別從左顯示5個字元寬度%msg:日誌訊息,%n是換行符-->
      <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
    </encoder>
    <!--日誌檔案最大的大小-->
    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
      <MaxFileSize>500MB</MaxFileSize>
    </triggeringPolicy>
    <!-- 只打印錯誤日誌 -->
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
      <level>error</level>
      <onMatch>ACCEPT</onMatch>
      <onMismatch>DENY</onMismatch>
    </filter>
  </appender>

  <!--連線資料庫配置-->
  <appender name="db_classic_mysql_pool" class="ch.qos.logback.classic.db.DBAppender">
    <connectionSource class="ch.qos.logback.core.db.DataSourceConnectionSource">
      <dataSource class="org.apache.commons.dbcp.BasicDataSource">
        <driverClassName>com.mysql.cj.jdbc.Driver</driverClassName>
        <url>jdbc:mysql://127.0.0.1:3306/logdb?serverTimezone=Asia/Shanghai</url>
        <username>root</username>
        <password>123456</password>
      </dataSource>
    </connectionSource>
  </appender>

  <!--myibatis log configure-->
  <logger name="com.apache.ibatis" level="TRACE"/>
  <logger name="java.sql.Connection" level="DEBUG" />
  <logger name="java.sql.Statement" level="DEBUG"/>
  <logger name="java.sql.PreparedStatement" level="DEBUG"/>

  <!-- 日誌輸出級別 -->
  <root level="INFO">
    <appender-ref ref="stdout" />
    <appender-ref ref="application_file" />
    <appender-ref ref="error_file"/>
    <appender-ref ref="db_classic_mysql_pool" />
  </root>

</configuration>

三、建立資料庫表

在ch.qos.logback.classic.db包下可以找到對應資料庫的表建立語句

我用的mysql資料庫,前提是要首先自己建立庫

mysql的資料庫sql語句:

BEGIN;
DROP TABLE IF EXISTS logging_event_property;
DROP TABLE IF EXISTS logging_event_exception;
DROP TABLE IF EXISTS logging_event;
COMMIT;


BEGIN;
CREATE TABLE logging_event 
 (
  timestmp     BIGINT NOT NULL,formatted_message TEXT NOT NULL,logger_name    VARCHAR(254) NOT NULL,level_string   VARCHAR(254) NOT NULL,thread_name    VARCHAR(254),reference_flag  SMALLINT,arg0       VARCHAR(254),arg1       VARCHAR(254),arg2       VARCHAR(254),arg3       VARCHAR(254),caller_filename  VARCHAR(254) NOT NULL,caller_class   VARCHAR(254) NOT NULL,caller_method   VARCHAR(254) NOT NULL,caller_line    CHAR(4) NOT NULL,event_id     BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY
 );
COMMIT;

BEGIN;
CREATE TABLE logging_event_property
 (
  event_id     BIGINT NOT NULL,mapped_key    VARCHAR(254) NOT NULL,mapped_value   TEXT,PRIMARY KEY(event_id,mapped_key),FOREIGN KEY (event_id) REFERENCES logging_event(event_id)
 );
COMMIT;

BEGIN;
CREATE TABLE logging_event_exception
 (
  event_id     BIGINT NOT NULL,i        SMALLINT NOT NULL,trace_line    VARCHAR(254) NOT NULL,i),FOREIGN KEY (event_id) REFERENCES logging_event(event_id)
 );
COMMIT;

建立好的表

四、測試

1、編寫測試程式碼

@RunWith(SpringRunner.class)
@SpringBootTest
public class Springboot02MybatisApplicationTests {

  private final Logger logger = LoggerFactory.getLogger(Springboot02MybatisApplicationTests.class);
  @Test
  public void contextLoads() {
    logger.info("資料庫日誌info");
    logger.error("資料庫日誌error");
  }
}

2、執行結果

預設儲存所有符合當前級別的日誌記錄

五、自定義資料庫表字段和儲存內容

當然,預設的表字段那麼多,儲存了很多內容,但是我們很多時候只是自己列印的日誌內容,為了節省磁碟空間,這個時候可以自定義儲存欄位和儲存內容

步驟:

1、建立資料庫表

DROP TABLE IF EXISTS `logging`;
CREATE TABLE `logging` (
 `id` BIGINT(20) NOT NULL AUTO_INCREMENT,`message` VARCHAR(300) NOT NULL COMMENT '內容',`level_string` VARCHAR(254) NOT NULL COMMENT '級別',`created_time` DATETIME NOT NULL COMMENT '時間',`logger_name` VARCHAR(300) NOT NULL COMMENT '全類名',PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8 COMMENT='自定義日誌記錄表'

2、重寫DBAppender類為LogDBAppender類

package com.me.study.springboot02mybatis.config;

import ch.qos.logback.classic.spi.CallerData;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.db.DBAppenderBase;
import org.springframework.context.annotation.Configuration;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;

@Configuration
public class LogDBAppender extends DBAppenderBase<ILoggingEvent> {

  protected static final Method GET_GENERATED_KEYS_METHOD;
  //插入sql
  protected String insertSQL;
  // message 日誌內容
  static final int MESSAGE = 1;
  // level_string
  static final int LEVEL_STRING = 2;
  // created_time 時間
  static final int CREATE_TIME = 3;
  // logger_name 全類名
  static final int LOGGER_NAME = 4;

  static final StackTraceElement EMPTY_CALLER_DATA = CallerData.naInstance();

  static {
    // PreparedStatement.getGeneratedKeys() method was added in JDK 1.4
    Method getGeneratedKeysMethod;
    try {
      // the
      getGeneratedKeysMethod = PreparedStatement.class.getMethod("getGeneratedKeys",(Class[]) null);
    } catch (Exception ex) {
      getGeneratedKeysMethod = null;
    }
    GET_GENERATED_KEYS_METHOD = getGeneratedKeysMethod;
  }

  @Override
  public void start() {
    // 將寫好的sql語句賦值給insertSQL
    insertSQL = buildInsertSQL();
    super.start();
  }

  // 自己寫新增sql語句
  private static String buildInsertSQL() {
    return "INSERT INTO `logging`(`message`,`level_string`,`created_time`,`logger_name`)" +
        "VALUES (?,?,?)";
  }

  @Override
  protected Method getGeneratedKeysMethod() {
    return GET_GENERATED_KEYS_METHOD;
  }

  @Override
  protected String getInsertSQL() {
    return insertSQL;
  }

  /**
   * 主要修改的方法
   *
   * @param stmt
   * @param event
   * @throws SQLException
   */
  private void bindLoggingEventWithInsertStatement(PreparedStatement stmt,ILoggingEvent event) throws SQLException {
    // event.getFormattedMessage() 日誌列印內容
    String message = event.getFormattedMessage();
    // 如果只想儲存自己列印的日誌,可以這樣寫日誌:logger.info("- XXXX")
    if(message.startsWith("-")){ // 判斷日誌訊息首字母為 - 的日誌,記錄到資料庫表
      stmt.setString(MESSAGE,message);
      // event.getLevel().toString() 日誌級別
      stmt.setString(LEVEL_STRING,event.getLevel().toString());
      // new Timestamp(event.getTimeStamp()) 時間
      stmt.setTimestamp(CREATE_TIME,new Timestamp(event.getTimeStamp()));
      // event.getLoggerName() 全類名
      stmt.setString(LOGGER_NAME,event.getLoggerName());
    }

  }

  @Override
  protected void subAppend(ILoggingEvent eventObject,Connection connection,PreparedStatement statement) throws Throwable {
    bindLoggingEventWithInsertStatement(statement,eventObject);
    // This is expensive... should we do it every time?
    int updateCount = statement.executeUpdate();
    if (updateCount != 1) {
      addWarn("Failed to insert loggingEvent");
    }
  }

  @Override
  protected void secondarySubAppend(ILoggingEvent eventObject,long eventId) throws Throwable {
  }
}

3、修改logback日誌檔案,引用自定義的LogDBAppender類

  <!--連線資料庫配置-->
  <appender name="db_classic_mysql_pool" class="com.me.study.springboot02mybatis.config.LogDBAppender">
    <connectionSource class="ch.qos.logback.core.db.DataSourceConnectionSource">
      <dataSource class="org.apache.commons.dbcp.BasicDataSource">
        <driverClassName>com.mysql.cj.jdbc.Driver</driverClassName>
        <url>jdbc:mysql://127.0.0.1:3306/logdb?serverTimezone=Asia/Shanghai</url>
        <username>root</username>
        <password>admin</password>
      </dataSource>
    </connectionSource>
  </appender>

4、測試執行

1)編寫測試程式碼

  @Test
  public void contextLoads() {
    logger.info("- 資料庫日誌info");
    logger.error("- 資料庫日誌error");
    logger.info("一條不帶‘-'的日誌,看會不會記錄如資料庫");
  }

2)執行結果

資料庫儲存結果只儲存了自定義的日誌記錄

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。