SpringBoot Logback日志记录到数据库的实现方法
作者:拉提娜的爸爸 发布时间:2024-01-16 11:58:11
标签: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,
PRIMARY KEY(event_id, 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, Connection connection, 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)运行结果
数据库存储结果只存储了自定义的日志记录
来源:https://www.jianshu.com/p/d8eea65626fc
0
投稿
猜你喜欢
- 设计页面时,经常会从一个页面打开一个子窗口以供浏览者查看。通常,这种子窗口中的内容一经浏览者看过,对于浏览者而言就不再需要,而他们常常会忘记
- 一、相同点dump 和 dumps 都实现了序列化load 和 loads 都实现反序列化变量从内存中变成可存储或传输的过程称之为序列化序列
- 记得上次电梯按钮讨论中有朋友提到日本的无序电梯,我没有太明白意思。除了各位大师提出的无厘头方案,也有不少超前的创意,好多都值得继续思考和探索
- 如何显示数据库的结构?<html><head><meta http-equiv="Cont
- 目录一、基本用法二、计数循环三、字符串遍历循环四、列表遍历循环五、文件遍历循环六、遍历循环的扩展模式一、基本用法for <循环变量&g
- 数据分析师肯定每天都被各种各样的数据数据报表搞得焦头烂额,老板的,运营的、产品的等等。而且大部分报表都是重复性的工作,这篇文章就是帮助大家如
- 在我们学习python的过程中,学习序列是一门必修课。当我们掌握了序列过后,便会学习常用的两个排序函数sort()与sorted()。但很少
- 本篇博客介绍利用python脚本实现视频分帧,并将每一帧保存到本地。主要基于opencv包来实现,在运行代码前确保opencv包已正确安装。
- 仿豆瓣分页原型(Javascript版)写了个分页的样式。自我感觉,这样的分页前后兼顾,对于用户的体验是蛮好使的Javascript分页代码
- 折线图是数据分析中非常常用的图形。其中,折线图主要是以折线的上升或下降来表示统计数量的增减变化的统计图。用于分析自变量和因变量之间的趋势关系
- 读文件打开文件(文件需要存在)#打开文件f = open("data.txt","r")  
- Qt Designer的介绍在PyQt中编写UI界面可以直接通过代码来实现,也可以通过Qt Designer来完成。Qt Designer的
- 一、Python官方标准库:Tkinter (必须了解)Python内置图形界面库——Tkinter
- # -*-coding:utf-8-*-import sys, os'''将当前进程fork为一个守护进程注意:如果
- 一、多层前向神经网络多层前向神经网络由三部分组成:输出层、隐藏层、输出层,每层由单元组成;输入层由训练集的实例特征向量传入,经过连接结点的权
- 前文学习:python数据结构:数据类型.python数据结构输入输出及控制和异常.今天我们来学习面向对象编程,面向对象这种编程方式非常重要
- 看看下面:<%Set objQuery = Server.CreateObject("ixss
- 其实不光是上面描述的情况会锁住表,还有很多种场景会使表放生死锁,解锁其实很简单,下面用一个示例来讲解: 1 首先创建一个测试用的表: 代码如
- 目录一:twisted中的adbapi1.1 两个主要方法1.2 使用实例二:结合scrapy中的pipelines一:twisted中的a
- 本文实例为大家分享了python3判断url链接是否为404的具体代码,供大家参考,具体内容如下import pymysqlimport t