SpringBoot Logback日志記錄到數(shù)據(jù)庫(kù)的實(shí)現(xiàn)方法
對(duì)于日志的處理,有時(shí)候需要把符合條件的日志計(jì)入數(shù)據(jù)庫(kù)中
一、添加pom依賴(lài)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 這個(gè)依賴(lài)必須存在,否則會(huì)報(bào)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>
二、創(chuàng)建logback配置文件
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<!--定義日志文件的存儲(chǔ)地址 勿在 LogBack 的配置中使用相對(duì)路徑-->
<property name="LOG_HOME" value="/home/admin" />
<!-- 控制臺(tái)輸出 -->
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化輸出:%d表示日期,%thread表示線(xiàn)程名,%-5level:級(jí)別從左顯示5個(gè)字符寬度%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>
<!--日志文件保留天數(shù)-->
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化輸出:%d表示日期,%thread表示線(xiàn)程名,%-5level:級(jí)別從左顯示5個(gè)字符寬度%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>
<!--日志文件保留天數(shù)-->
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化輸出:%d表示日期,%thread表示線(xiàn)程名,%-5level:級(jí)別從左顯示5個(gè)字符寬度%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>
<!-- 只打印錯(cuò)誤日志 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>error</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!--連接數(shù)據(jù)庫(kù)配置-->
<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"/>
<!-- 日志輸出級(jí)別 -->
<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>
三、創(chuàng)建數(shù)據(jù)庫(kù)表
在ch.qos.logback.classic.db包下可以找到對(duì)應(yīng)數(shù)據(jù)庫(kù)的表創(chuàng)建語(yǔ)句

我用的mysql數(shù)據(jù)庫(kù),前提是要首先自己創(chuàng)建庫(kù)
mysql的數(shù)據(jù)庫(kù)sql語(yǔ)句:
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, PRIMARY KEY(event_id, i), FOREIGN KEY (event_id) REFERENCES logging_event(event_id) ); COMMIT;

創(chuàng)建好的表
四、測(cè)試
1、編寫(xiě)測(cè)試代碼
@RunWith(SpringRunner.class)
@SpringBootTest
public class Springboot02MybatisApplicationTests {
private final Logger logger = LoggerFactory.getLogger(Springboot02MybatisApplicationTests.class);
@Test
public void contextLoads() {
logger.info("數(shù)據(jù)庫(kù)日志info");
logger.error("數(shù)據(jù)庫(kù)日志error");
}
}
2、運(yùn)行結(jié)果

默認(rèn)存儲(chǔ)所有符合當(dāng)前級(jí)別的日志記錄
五、自定義數(shù)據(jù)庫(kù)表字段和存儲(chǔ)內(nèi)容
當(dāng)然,默認(rèn)的表字段那么多,存儲(chǔ)了很多內(nèi)容,但是我們很多時(shí)候只是自己打印的日志內(nèi)容,為了節(jié)省磁盤(pán)空間,這個(gè)時(shí)候可以自定義存儲(chǔ)字段和存儲(chǔ)內(nèi)容
步驟:
1、創(chuàng)建數(shù)據(jù)庫(kù)表
DROP TABLE IF EXISTS `logging`; CREATE TABLE `logging` ( `id` BIGINT(20) NOT NULL AUTO_INCREMENT, `message` VARCHAR(300) NOT NULL COMMENT '內(nèi)容', `level_string` VARCHAR(254) NOT NULL COMMENT '級(jí)別', `created_time` DATETIME NOT NULL COMMENT '時(shí)間', `logger_name` VARCHAR(300) NOT NULL COMMENT '全類(lèi)名', PRIMARY KEY (`id`) ) ENGINE=INNODB DEFAULT CHARSET=utf8 COMMENT='自定義日志記錄表'
2、重寫(xiě)DBAppender類(lèi)為L(zhǎng)ogDBAppender類(lèi)
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 日志內(nèi)容
static final int MESSAGE = 1;
// level_string
static final int LEVEL_STRING = 2;
// created_time 時(shí)間
static final int CREATE_TIME = 3;
// logger_name 全類(lèi)名
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() {
// 將寫(xiě)好的sql語(yǔ)句賦值給insertSQL
insertSQL = buildInsertSQL();
super.start();
}
// 自己寫(xiě)新增sql語(yǔ)句
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() 日志打印內(nèi)容
String message = event.getFormattedMessage();
// 如果只想存儲(chǔ)自己打印的日志,可以這樣寫(xiě)日志:logger.info("- XXXX")
if(message.startsWith("-")){ // 判斷日志消息首字母為 - 的日志,記錄到數(shù)據(jù)庫(kù)表
stmt.setString(MESSAGE, message);
// event.getLevel().toString() 日志級(jí)別
stmt.setString(LEVEL_STRING, event.getLevel().toString());
// new Timestamp(event.getTimeStamp()) 時(shí)間
stmt.setTimestamp(CREATE_TIME, new Timestamp(event.getTimeStamp()));
// event.getLoggerName() 全類(lèi)名
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, Connection connection, long eventId) throws Throwable {
}
}
3、修改logback日志文件,引用自定義的LogDBAppender類(lèi)
<!--連接數(shù)據(jù)庫(kù)配置-->
<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、測(cè)試運(yùn)行
1)編寫(xiě)測(cè)試代碼
@Test
public void contextLoads() {
logger.info("- 數(shù)據(jù)庫(kù)日志info");
logger.error("- 數(shù)據(jù)庫(kù)日志error");
logger.info("一條不帶‘-'的日志,看會(huì)不會(huì)記錄如數(shù)據(jù)庫(kù)");
}
2)運(yùn)行結(jié)果

數(shù)據(jù)庫(kù)存儲(chǔ)結(jié)果只存儲(chǔ)了自定義的日志記錄
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
spring AOP的After增強(qiáng)實(shí)現(xiàn)方法實(shí)例分析
這篇文章主要介紹了spring AOP的After增強(qiáng)實(shí)現(xiàn)方法,結(jié)合實(shí)例形式分析了spring面向切面AOP的After增強(qiáng)實(shí)現(xiàn)步驟與相關(guān)操作技巧,需要的朋友可以參考下2020-01-01
spring boot idea maven依賴(lài)找不到問(wèn)題處理方法
這篇文章主要介紹了spring boot idea 偶爾maven依賴(lài)找不到問(wèn)題,這里總結(jié)了幾種處理方法,方便嘗試排查,對(duì)spring boot idea maven依賴(lài)找不到問(wèn)題感興趣的朋友跟隨小編一起看看吧2023-08-08
Java中Comparable與Comparator的區(qū)別解析
這篇文章主要介紹了Java中Comparable與Comparator的區(qū)別解析,實(shí)現(xiàn)Comparable接口,重寫(xiě)compareTo方法,一般在實(shí)體類(lèi)定義的時(shí)候就可以選擇實(shí)現(xiàn)該接口,提供一個(gè)默認(rèn)的排序方式,供Arrays.sort和Collections.sort使用,需要的朋友可以參考下2024-01-01
JDK安裝與配置超級(jí)詳細(xì)教程(包含二個(gè)或多個(gè)JDK的同時(shí)安裝)
這篇文章主要給大家介紹了關(guān)于JDK安裝與配置(包含二個(gè)或多個(gè)JDK的同時(shí)安裝)的相關(guān)資料,對(duì)于Java學(xué)習(xí)者來(lái)說(shuō),一臺(tái)電腦拿到手肯定要配置JDK,但是對(duì)于新手來(lái)說(shuō)還是容易出錯(cuò),需要的朋友可以參考下2023-10-10
java實(shí)現(xiàn)大文件導(dǎo)出的實(shí)現(xiàn)與優(yōu)化
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)大文件導(dǎo)出的實(shí)現(xiàn)與優(yōu)化的相關(guān)資料,文中的示例代碼講解詳細(xì),對(duì)我們深入了解java有一定的幫助,感興趣的小伙伴可以了解下2023-11-11
Springboot集成mybatis與jsp過(guò)程詳解
這篇文章主要介紹了Springboot集成mybatis與jsp過(guò)程,Spring Boot是一種全新的框架(相對(duì)而言),是用來(lái)簡(jiǎn)化Spring應(yīng)用的初始搭建以及開(kāi)發(fā)過(guò)程。該框架使用了特定的方式來(lái)進(jìn)行配置2021-09-09
SpringBoot整合Redis及Redis工具類(lèi)撰寫(xiě)實(shí)例
這篇文章主要介紹了SpringBoot整合Redis及Redis工具類(lèi)撰寫(xiě)實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01

