SpringBoot Logback日志記錄到數(shù)據(jù)庫的實(shí)現(xiàn)方法
對于日志的處理,有時候需要把符合條件的日志計入數(shù)據(jù)庫中
一、添加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>
二、創(chuàng)建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> <!--日志文件保留天數(shù)--> <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> <!--日志文件保留天數(shù)--> <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> <!--連接數(shù)據(jù)庫配置--> <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>
三、創(chuàng)建數(shù)據(jù)庫表
在ch.qos.logback.classic.db包下可以找到對應(yīng)數(shù)據(jù)庫的表創(chuàng)建語句
我用的mysql數(shù)據(jù)庫,前提是要首先自己創(chuàng)建庫
mysql的數(shù)據(jù)庫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, PRIMARY KEY(event_id, i), FOREIGN KEY (event_id) REFERENCES logging_event(event_id) ); COMMIT;
創(chuàng)建好的表
四、測試
1、編寫測試代碼
@RunWith(SpringRunner.class) @SpringBootTest public class Springboot02MybatisApplicationTests { private final Logger logger = LoggerFactory.getLogger(Springboot02MybatisApplicationTests.class); @Test public void contextLoads() { logger.info("數(shù)據(jù)庫日志info"); logger.error("數(shù)據(jù)庫日志error"); } }
2、運(yùn)行結(jié)果
默認(rèn)存儲所有符合當(dāng)前級別的日志記錄
五、自定義數(shù)據(jù)庫表字段和存儲內(nèi)容
當(dāng)然,默認(rèn)的表字段那么多,存儲了很多內(nèi)容,但是我們很多時候只是自己打印的日志內(nèi)容,為了節(jié)省磁盤空間,這個時候可以自定義存儲字段和存儲內(nèi)容
步驟:
1、創(chuàng)建數(shù)據(jù)庫表
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 '級別', `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 日志內(nèi)容 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() 日志打印內(nèi)容 String message = event.getFormattedMessage(); // 如果只想存儲自己打印的日志,可以這樣寫日志:logger.info("- XXXX") if(message.startsWith("-")){ // 判斷日志消息首字母為 - 的日志,記錄到數(shù)據(jù)庫表 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, Connection connection, long eventId) throws Throwable { } }
3、修改logback日志文件,引用自定義的LogDBAppender類
<!--連接數(shù)據(jù)庫配置--> <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、測試運(yùn)行
1)編寫測試代碼
@Test public void contextLoads() { logger.info("- 數(shù)據(jù)庫日志info"); logger.error("- 數(shù)據(jù)庫日志error"); logger.info("一條不帶‘-'的日志,看會不會記錄如數(shù)據(jù)庫"); }
2)運(yùn)行結(jié)果
數(shù)據(jù)庫存儲結(jié)果只存儲了自定義的日志記錄
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
JDK10新特性之var泛型和多個接口實(shí)現(xiàn)方法
這篇文章主要介紹了JDK10的新特性:var泛型和多個接口實(shí)現(xiàn)方法,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-05-05Java連接合并2個數(shù)組(Array)的5種方法例子
最近在寫代碼時遇到了需要合并兩個數(shù)組的需求,突然發(fā)現(xiàn)以前沒用過,于是研究了一下合并數(shù)組的方式,這篇文章主要給大家介紹了關(guān)于Java連接合并2個數(shù)組(Array)的5種方法,需要的朋友可以參考下2023-12-12詳解java CountDownLatch和CyclicBarrier在內(nèi)部實(shí)現(xiàn)和場景上的區(qū)別
這篇文章主要介紹了詳解java CountDownLatch和CyclicBarrier在內(nèi)部實(shí)現(xiàn)和場景上的區(qū)別,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05Sentinel熱門詞匯限流的實(shí)現(xiàn)詳解
這篇文章主要介紹了使用Sentinel對熱門詞匯進(jìn)行限流的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07Idea跑的項(xiàng)目沒問題將程序install成jar包運(yùn)行報錯空指針的問題
這篇文章主要介紹了Idea跑的項(xiàng)目沒問題,將程序install成jar包運(yùn)行報錯空指針的問題,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-06-06微信小程序+后端(java)實(shí)現(xiàn)開發(fā)
這篇文章主要介紹了微信小程序+后端(java)實(shí)現(xiàn)開發(fā),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04