当前位置: 首页 > news >正文

Logback自定义DBAppender保存系统日志到数据库

在系统中采用了spring boot + logback+slf4j的日志框架,将系统日志记录到数据库。
在这里插入图片描述

相关参考来源:

官方文档-DBAppender
Logback输出日志到自定义MySQL数据库(重写DBAppender)
logback日志框架中filter的使用

1. 添加依赖:

从 logback 版本 1.2.8 开始,DBAppender 不再随 logback-classic 一起提供,所以需要单独引入

<dependency><groupId>ch.qos.logback.db</groupId><artifactId>logback-classic-db</artifactId><version>1.2.11.1</version>
</dependency>

2. 自定义表结构:

可以根据实际情况增加或减少和修改字段

CREATE TABLE t_log_logback (`id` VARCHAR ( 64 ) NOT NULL COMMENT '主键',`create_time` VARCHAR ( 32 ) NOT NULL COMMENT '创建时间',`message` TEXT NOT NULL COMMENT '内容',`level_string` VARCHAR ( 254 ) NOT NULL COMMENT '日志等级:TRACE,DEBUG,INFO,WARNING,ERROR,FATAL',`logger_name` VARCHAR ( 254 ) NOT NULL COMMENT '发出日志记录请求的记录器的名称',`thread_name` VARCHAR ( 254 ) COMMENT '线程名称',`reference_flag` INT ( 11 ) COMMENT 'MDC属性',`caller_filename` VARCHAR ( 254 ) NOT NULL COMMENT '发出日志记录请求的文件名',`caller_class` VARCHAR ( 254 ) NOT NULL COMMENT '发出日志记录请求的类',`caller_method` VARCHAR ( 254 ) NOT NULL COMMENT '发出日志记录请求的方法的名称',`caller_line` VARCHAR ( 4 ) NOT NULL COMMENT '发出日志记录请求的行号',
PRIMARY KEY ( `id` ) USING BTREE 
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = DYNAMIC COMMENT = 'logback系统日志记录表';

3. 自定义追加器DbLogbackAppender

import ch.qos.logback.classic.spi.CallerData;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.db.DBAppenderBase;import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;/*** <p>* 	自定义DB日志追加器,参考实现类 {@link ch.qos.logback.classic.db.DBAppender}*  参考来源:https://blog.csdn.net/qq_20914913/article/details/92830914* </p>*/
public class DbLogbackAppender extends DBAppenderBase<ILoggingEvent> {private String insertSQL;private static final Method GET_GENERATED_KEYS_METHOD;// 对应于数据库字段的插入数据序号private static final int ID_INDEX = 1;private static final int CREATE_TIME_INDEX = 2;private static final int MESSAGE_INDEX = 3;private static final int LEVEL_STRING_INDEX = 4;private static final int LOGGER_NAME_INDEX = 5;private static final int THREAD_NAME_INDEX = 6;private static final int REFERENCE_FLAG_INDEX = 7;private static final int CALLER_FILENAME_INDEX = 8;private static final int CALLER_CLASS_INDEX = 9;private static final int CALLER_METHOD_INDEX = 10;private static final int CALLER_LINE_INDEX = 11;private static final StackTraceElement EMPTY_CALLER_DATA = CallerData.naInstance();// 处理主键的自动生成,这里我们使用手工生成,因此下面代码可忽略static {// PreparedStatement.getGeneratedKeys() method was added in JDK 1.4Method getGeneratedKeysMethod;try {// thegetGeneratedKeysMethod = PreparedStatement.class.getMethod("getGeneratedKeys", (Class[]) null);} catch (Exception ex) {getGeneratedKeysMethod = null;}GET_GENERATED_KEYS_METHOD = getGeneratedKeysMethod;}@Overridepublic void start() {insertSQL = buildInsertSQL();cnxSupportsBatchUpdates = connectionSource.supportsBatchUpdates();// super.start();super.started = true;}// 核心代码,构建插入语句,并对应数据库字段private static String buildInsertSQL() {return "INSERT INTO t_log_logback " +"(id, create_time, message, level_string, logger_name, thread_name, " +"reference_flag, caller_filename, caller_class, caller_method, caller_line) "+"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";}// 管理每个字段插入的数据private void bindLoggingEventWithInsertStatement(PreparedStatement stmt, ILoggingEvent event) throws SQLException {// TODO 手工处理ID的生成stmt.setString(ID_INDEX, IdUtils.simpleUUID());stmt.setString(CREATE_TIME_INDEX, LocalDateTime.ofInstant(Instant.ofEpochMilli(event.getTimeStamp()),ZoneId.systemDefault()).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));stmt.setString(MESSAGE_INDEX, event.getFormattedMessage());stmt.setString(LEVEL_STRING_INDEX, event.getLevel().toString());stmt.setString(LOGGER_NAME_INDEX, event.getLoggerName());stmt.setString(THREAD_NAME_INDEX, event.getThreadName());}// 管理每个字段插入的数据private void bindCallerDataWithPreparedStatement(PreparedStatement stmt, StackTraceElement[] callerDataArray) throws SQLException {StackTraceElement caller = extractFirstCaller(callerDataArray);stmt.setInt(REFERENCE_FLAG_INDEX, 0);stmt.setString(CALLER_FILENAME_INDEX, caller.getFileName());stmt.setString(CALLER_CLASS_INDEX, caller.getClassName());stmt.setString(CALLER_METHOD_INDEX, caller.getMethodName());stmt.setString(CALLER_LINE_INDEX, Integer.toString(caller.getLineNumber()));}// 核心方法,插入具体日志数据@Overrideprotected void subAppend(ILoggingEvent event, Connection connection, PreparedStatement insertStatement) throws Throwable {bindLoggingEventWithInsertStatement(insertStatement, event);// This is expensive... should we do it every time?bindCallerDataWithPreparedStatement(insertStatement, event.getCallerData());int updateCount = insertStatement.executeUpdate();if (updateCount != 1) {addWarn("Failed to insert loggingEvent");}}private StackTraceElement extractFirstCaller(StackTraceElement[] callerDataArray) {StackTraceElement caller = EMPTY_CALLER_DATA;if (hasAtLeastOneNonNullElement(callerDataArray))caller = callerDataArray[0];return caller;}private boolean hasAtLeastOneNonNullElement(StackTraceElement[] callerDataArray) {return callerDataArray != null && callerDataArray.length > 0 && callerDataArray[0] != null;}@Overrideprotected Method getGeneratedKeysMethod() {return GET_GENERATED_KEYS_METHOD;}@Overrideprotected String getInsertSQL() {return insertSQL;}protected void secondarySubAppend(ILoggingEvent event, Connection connection, long eventId) throws Throwable {}@Overrideprotected long selectEventId(PreparedStatement insertStatement, Connection connection) throws SQLException, InvocationTargetException {return 0;}
}

4. logback-spring.xml配置关联

添加自定义的追加器DbLogbackAppender

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false"><!-- 获取springboot中的数据库配置 --><springProperty scope="context" name="driverClassName" source="spring.datasource.master.driverClassName" defaultValue="driverClassName"/><springProperty scope="context" name="url" source="spring.datasource.master.url" defaultValue="url"/><springProperty scope="context" name="username" source="spring.datasource.master.username" defaultValue="username"/><springProperty scope="context" name="encryptPassword" source="spring.datasource.master.encryptPassword" defaultValue="encryptPassword"/><!-- 添加自定义DbLogbackAppender --><appender name="DB" class="com.xxx.DbLogbackAppender"><connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource"><driverClass>${driverClassName}</driverClass><url>${url}</url><user>${username}</user><password>${password}</password></connectionSource><!--临界值过滤。只记录指定级别以及高于该级别的日志--><filter class="ch.qos.logback.classic.filter.ThresholdFilter"><level>WARN</level></filter></appender><root level="DEBUGE"><appender-ref ref="DB" /></root>
</configuration>

到这里,就配置完成了,重启系统,访问日志,会将warn级别以上的日志记录到数据库表。

由于数据源是使用logback的LogbackConnectionSource,如果要自定义数据源,请往下看

5.自定义数据源

创建类LogbackConnectionSource.java,继承ConnectionSourceBase


import ch.qos.logback.core.db.ConnectionSourceBase;import java.sql.Connection;
import java.sql.SQLException;/*** <p>*  获取数据库连接用于logback,并提供DbLogbackAppender使用*  参考类:ch.qos.logback.core.db.DriverManagerConnectionSource* </p>*/
public class LogbackConnectionSource extends ConnectionSourceBase {public Connection getConnection() {获取数据库连接Connection connection = null;// 自行构建连接// ...return connection;}
}

修改logback-spring.xml配置为如下

	<!-- 添加自定义DbLogbackAppender --><appender name="DB" class="com.xxx.DbLogbackAppender"><!-- 添加自定义的数据源 --><connectionSource class="com.xxx.LogbackConnectionSource"><!-- <driverClass>${driverClassName}</driverClass><url>${url}</url><user>${username}</user><password>${password}</password>--></connectionSource><!--临界值过滤。只记录指定级别以及高于该级别的日志--><filter class="ch.qos.logback.classic.filter.ThresholdFilter"><level>WARN</level></filter></appender>

相关文章:

Logback自定义DBAppender保存系统日志到数据库

在系统中采用了spring boot logback&#xff0b;slf4j的日志框架&#xff0c;将系统日志记录到数据库。 相关参考来源&#xff1a; 官方文档-DBAppender Logback输出日志到自定义MySQL数据库&#xff08;重写DBAppender&#xff09; logback日志框架中filter的使用 1. 添加依…...

云原生之使用Docker部署LimeSurvey在线调查工具

云原生之使用Docker部署LimeSurvey在线调查工具 一、LimeSurvey介绍1.1 LimeSurvey简介1.2 LimeSurvey特点1.3 LimeSurvey使用场景1.4 LimeSurvey支持版本二、本地环境介绍2.1 本地环境规划2.2 本次实践介绍三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.检查doc…...

sdbusplus:添加ObjectManager interface

ObjectManager接口可以一次性拿到对象及子对象的所有property,在交互中经常会用到。 sdbusplus提供了add_manager完成该接口的添加: //server_obj.cpp #include <sdbusplus/asio/connection.hpp> #include <sdbusplus/asio/object_server.hpp> #include <sd…...

“RAID0 vs RAID1 vs RAID5 vs RAID6 vs RAID10:哪种RAID级别最适合你的需求?“

概要&#xff1a; RAID&#xff08;Redundant Array of Independent Disks&#xff09;是一种数据存储技术&#xff0c;可以将多个硬盘组合起来以提高性能、可靠性和容错能力。下面是几种常见的RAID级别&#xff0c;以及它们的用途和特点。 目录 RAID 0RAID 1RAID 5RAID 6RAID…...

【MySQL】Mycat

文章目录 什么是Mycat为什么要用Mycatmycat能干什么各数据库中间件对比Mycat原理数据库中间件逻辑库逻辑表分片表分片规则全局表ER表非分片表分片节点节点主机mycat安装mycat核心配置schema.xmlserver.xmlrule.xml加密明文密码&#xff08;可选&#xff09; MyCat读写分离垂直拆…...

Netty中ServerBootstrap类介绍

一、Netty基本介绍 Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具&#xff0c;用以快速开发高性能、高可靠性的网络服务器和客户端程序。Netty 在保证易于开发的同时还保证了其应用的性能&#xff0c;稳定性和伸缩性。 Netty 是一…...

数字图像处理实验报告

目录 实验二、图像在空间域上的处理方法 实验三、图像在频率域上的处理方法 实验二、图像在空间域上的处理方法 一、实验目的 了解图像亮&#xff08;灰&#xff09;度变换与空间滤波的意义和手段&#xff1b;熟悉图像亮&#xff08;灰&#xff09;度变换与空间滤波的MATLA…...

【C51】10-基础51单片机的小车项目(51完结)

10.1小车的安装 10.2电机模块的开发&#xff08;L9110S&#xff09; 接通 VCC &#xff0c; GND 模块电源指示灯亮&#xff0c; 以下资料来源官方&#xff0c;但是不对&#xff0c;根据下节课实际调试 IA1 输入高电平&#xff0c; IA1 输入低电平&#xff0c;【 OA1 OB1 】电…...

进程、线程、锁阶段总结汇总

目录 进程 线程 锁 由于进程线程和锁的方面比较陌生&#xff0c;并且繁杂&#xff0c;所以简单总结一下学习到的函数API 进程 子进程创建 fork(); 进程结束 exit(); 进程回收 wait(); 进程回收 waitpad(); //函数可以指定进程组中的任意子进程&#xff0c;可以设置特殊…...

Filters.jar图片转素描

链接&#xff1a;https://pan.baidu.com/s/1ATlC2l1I83TPYFomHiWuFg?pwd2vm5 提取码&#xff1a;2vm5...

将MSYS2 MinGW集成到Windows终端

微软开发了一款Windows终端的开源软件&#xff0c;非常好用。安装后在Win7及以上系统会在右键菜单中添加一条“在终端中打开”的命令&#xff0c;非常方便。它默认配置了Windows命令行以及PowerShell&#xff0c;如果安装了Visual Studio 2022还会配置Visual Studio 2022的命令…...

SpringBoot项目使用slf4j的MDC日志打点功能

SpringBoot项目使用slf4j的MDC日志打点功能 物料准备&#xff1a; 1.自定义1个线程MDC打点工具类 2.配置logback打印MDC打点的traceId 3.配置webMVC使用MDC打点 4.配置ThreadPoolTaskExecutor使用MDC打点 5.配置HttpClient使用MDC打点 6.测试MDC日志打点效果 线程mdc打…...

宝塔修改默认端口后面板打不开

1、查看防火墙开启的端口&#xff0c;发现没有开启8888 [rootVM-12-12-centos ~]# firewall-cmd --list-ports 20/tcp 21/tcp 22/tcp 80/tcp 888/tcp 8081/tcp 39000-40000/tcp 8081/udp 2、防火墙开启8888端口 [rootVM-12-12-centos ~]# firewall-cmd --zonepublic --add-por…...

tinkerCAD案例:3.基本按钮

基本按钮 在本课中&#xff0c;您将学习制作具有圆柱形状的基本按钮。 说明 将圆柱体拖动到工作平面。 将其缩小到 2 毫米的高度。 提示&#xff1a; 您可以使用圆柱形状顶部的白点缩小圆柱体。 将其缩小到直径 16 毫米。 这将是按钮的主要形状。 现在我们可以创建允许将纽…...

客户线上反馈:从信息搜集到疑难 bug 排查全流程经验分享

写在前面&#xff1a;本文是我在前端团队的第三次分享&#xff0c;应该很少会有开发者写客户反馈处理流程以及 bug 排查的心得技巧&#xff0c;全文比较长&#xff0c;写了一个多星期大概1W多字&#xff08;也是我曾经2年工作的总结&#xff09;&#xff0c;如果你有耐心阅读&a…...

悲观锁、乐观锁、自旋锁

悲观锁、乐观锁、自旋锁 &#xff08;1&#xff09;乐观锁 乐观锁是一种乐观的思想&#xff0c;即认为读多写少&#xff0c;遇到并发的可能性低&#xff0c;每次拿数据时都认为别人不会修改&#xff0c;所以不会上锁&#xff0c;但是在更新的时候会判断一下在此期间别人有没有…...

七、进程地址空间

一、环境变量 &#xff08;一&#xff09;概念 环境变量(environment variables)&#xff1a;系统当中用做特殊用途的系统变量。 如&#xff1a;我们在编写C/C代码的时候&#xff0c;在链接的时候&#xff0c;从来不知道我们的所链接的动态静态库在哪里&#xff0c;但是照样可…...

浅谈智能微电网供电系统的谐波治理

摘要&#xff1a;智能微电网供电系统的特性容易引发谐波&#xff0c;而谐波导致电力损耗加大&#xff0c;降低供电质量。本文从谐波的产 生原因和危害做出详细阐述&#xff0c;并结合智能微电网提出了治 理谐波的方法和措施。 关键词&#xff1a;智能微电网&#xff1b;谐波危害…...

springboot项目的社区/博客系统

课前导读&#xff1a; 你学完一篇&#xff0c;你就多会一项技能&#xff0c;多多少少对你还是有点帮助的不是吗&#xff1f;~~~ 这是博主网页的url&#xff1a;优文共享社区 开发环境&#xff1a;JDK1.8&#xff0c;IDEA2021&#xff0c;MySQL5.7&#xff0c;Windows11 开发技术…...

go语言基础——函数、结构体、接口

由于go不是一门面向对象的语言&#xff0c;因此在有一些特性上和java是有一些区别的&#xff0c;比如go中就没有类这样的概念。下面来介绍一下go的一些特性。 结构体 结构体类似与java中的类&#xff0c;但又不完全一样。在类中&#xff0c;可以定义字段和方法&#xff0c;但…...

RocketMQ延迟消息机制

两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数&#xff0c;对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后&#xf…...

SciencePlots——绘制论文中的图片

文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了&#xff1a;一行…...

ElasticSearch搜索引擎之倒排索引及其底层算法

文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...

MySQL 8.0 OCP 英文题库解析(十三)

Oracle 为庆祝 MySQL 30 周年&#xff0c;截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始&#xff0c;将英文题库免费公布出来&#xff0c;并进行解析&#xff0c;帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

select、poll、epoll 与 Reactor 模式

在高并发网络编程领域&#xff0c;高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表&#xff0c;以及基于它们实现的 Reactor 模式&#xff0c;为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。​ 一、I…...

接口自动化测试:HttpRunner基础

相关文档 HttpRunner V3.x中文文档 HttpRunner 用户指南 使用HttpRunner 3.x实现接口自动化测试 HttpRunner介绍 HttpRunner 是一个开源的 API 测试工具&#xff0c;支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议&#xff0c;涵盖接口测试、性能测试、数字体验监测等测试类型…...

免费数学几何作图web平台

光锐软件免费数学工具&#xff0c;maths,数学制图&#xff0c;数学作图&#xff0c;几何作图&#xff0c;几何&#xff0c;AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...

MyBatis中关于缓存的理解

MyBatis缓存 MyBatis系统当中默认定义两级缓存&#xff1a;一级缓存、二级缓存 默认情况下&#xff0c;只有一级缓存开启&#xff08;sqlSession级别的缓存&#xff09;二级缓存需要手动开启配置&#xff0c;需要局域namespace级别的缓存 一级缓存&#xff08;本地缓存&#…...

LCTF液晶可调谐滤波器在多光谱相机捕捉无人机目标检测中的作用

中达瑞和自2005年成立以来&#xff0c;一直在光谱成像领域深度钻研和发展&#xff0c;始终致力于研发高性能、高可靠性的光谱成像相机&#xff0c;为科研院校提供更优的产品和服务。在《低空背景下无人机目标的光谱特征研究及目标检测应用》这篇论文中提到中达瑞和 LCTF 作为多…...

Java数组Arrays操作全攻略

Arrays类的概述 Java中的Arrays类位于java.util包中&#xff0c;提供了一系列静态方法用于操作数组&#xff08;如排序、搜索、填充、比较等&#xff09;。这些方法适用于基本类型数组和对象数组。 常用成员方法及代码示例 排序&#xff08;sort&#xff09; 对数组进行升序…...