SLF4J+Log4j2日志存入MySQL配置教程
在Springboot项目中配置日志持久化至数据库时,我踩过不少坑。尝试多种方案后,发现Log4j2的JDBC Appender确实高效可靠。
项目基于Springboot 2.2.3.RELEASE,日志门面选用SLF4J 1.7.30,实现框架搭配JUL与Log4j 2.12.1(务必排除自带的Logback)。同时需要引入log4j-slf4j桥接包,版本同样锁定2.12.1。
参照官方文档,整体实现逻辑非常清晰。
文档直言核心在于获取一个有效的java.sql.Connection,并提供了可直接复用的配置模板。
Appenders配置详解
先看Appenders部分的配置。关键属性包括:tableName指定目标表名,class定义获取Connection的工厂类,method指明工厂类中的方法名。
每个Column标签支持三种模式:literal直接写入SQL字面量(如数据库序列值),pattern从日志事件中提取字段,isEventTimestamp用于标记时间戳列。
配置示例:
Connection获取实战
官方示例使用Apache DBCP连接池实现Connection获取。关键在于Log4j2仅负责调用你提供的工厂方法,连接池管理、事务控制、自动重连等全部需要自行实现。
示例代码:
package net.example.db;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp.DriverManagerConnectionFactory;
import org.apache.commons.dbcp.PoolableConnection;
import org.apache.commons.dbcp.PoolableConnectionFactory;
import org.apache.commons.dbcp.PoolingDataSource;
import org.apache.commons.pool.impl.GenericObjectPool;
public class ConnectionFactory {
private static interface Singleton {
final ConnectionFactory INSTANCE = new ConnectionFactory();
}
private final DataSource dataSource;
private ConnectionFactory() {
Properties properties = new Properties();
properties.setProperty("user", "logging");
properties.setProperty("password", "abc123");
GenericObjectPool pool = new GenericObjectPool();
DriverManagerConnectionFactory connectionFactory = new DriverManagerConnectionFactory(
"jdbc:mysql://example.org:3306/exampleDb", properties);
new PoolableConnectionFactory(connectionFactory, pool, null, "SELECT 1", 3, false, false,
Connection.TRANSACTION_READ_COMMITTED);
this.dataSource = new PoolingDataSource(pool);
}
public static Connection getDatabaseConnection() throws SQLException {
return Singleton.INSTANCE.dataSource.getConnection();
}
} 到这里配置似乎完成了,但先别急着收工,有几个关键陷阱必须提前规避。
第一,Log4j2虽然给出了连接池参考,但它本身不管理连接池生命周期、事务边界和断线重连。这意味着你提供的工厂方法必须在任何场景下都能返回有效连接。一旦连接被关闭,框架不会自动恢复,需要自己编写判活与重建逻辑。
第二,实际项目中最常见的问题:很多项目已集成Druid等连接池,但Log4j2写入日志时,这些连接池可能尚未完成初始化(或者加载顺序错位)。结果日志无法写入,异常还被静默吞掉。务必规划好Bean的加载时序。
这里分享一个我在生产环境中使用的简化实现,比官方DBCP示例更直观:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class ConnectionFactory {
private static Connection connection = null;
public static Connection getDatabaseConnection() {
try {
if (connection == null || connection.isClosed()) {
synchronized (ConnectionFactory.class) {
if (connection == null || connection.isClosed()) {
connection = DriverManager.getConnection(
"jdbc:mysql://**.**.cn:3306/**?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai",
"**", "**");
}
}
}
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
}配套建表SQL简洁直观:
create table tbl_log (
ID int auto_increment primary key,
EVENT_DATE datetime null,
LEVEL varchar(20) null,
LOGGER varchar(20) null,
MESSAGE text null,
THROWABLE text null
);最后查看实际运行效果:
整体思路并不复杂,但细节疏忽极易翻车。尤其框架加载顺序与连接有效性管理,强烈建议在正式环境反复压测后再部署上线。

