Spring Boot中如何为每个数据库请求记录DB连接详情?
实现多数据源请求的数据库连接详情日志记录
完全可以实现,以下是几种不同技术栈下的具体方案,按需选择:
1. Spring Boot 动态数据源路由场景(AbstractRoutingDataSource)
如果你的多数据源是基于AbstractRoutingDataSource实现的动态路由,可直接在路由逻辑中加入日志:
- 自定义数据源路由类,重写
determineCurrentLookupKey()方法,在返回数据源标识前后,提取对应数据源的配置信息并打印 - 示例代码:
public class DynamicDataSource extends AbstractRoutingDataSource { private static final Logger log = LoggerFactory.getLogger(DynamicDataSource.class); @Override protected Object determineCurrentLookupKey() { String dataSourceKey = DataSourceContextHolder.getDataSourceKey(); // 获取目标数据源实例 DataSource targetDs = (DataSource) getTargetDataSources().get(dataSourceKey); if (targetDs instanceof HikariDataSource) { HikariDataSource hikariDs = (HikariDataSource) targetDs; // 调试场景打印,生产环境建议脱敏密码部分 log.debug("当前请求使用数据源: {}, 用户名: {}, 连接字符串: {}", dataSourceKey, hikariDs.getUsername(), hikariDs.getJdbcUrl().replaceAll("password=.*?&", "password=***&")); } return dataSourceKey; } }
2. JDBC 层面拦截方案
通过代理模式拦截Connection获取操作,记录连接详情:
- 实现
InvocationHandler包装原始Connection,在执行SQL操作时打印连接信息 - 示例代码:
public class ConnectionLoggingProxy implements InvocationHandler { private static final Logger log = LoggerFactory.getLogger(ConnectionLoggingProxy.class); private final Connection targetConn; private final String username; private final String jdbcUrl; public ConnectionLoggingProxy(Connection target, String username, String jdbcUrl) { this.targetConn = target; this.username = username; this.jdbcUrl = jdbcUrl.replaceAll("password=.*?&", "password=***&"); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 拦截SQL执行相关方法 if ("prepareStatement".equals(method.getName()) || "createStatement".equals(method.getName())) { log.debug("执行SQL操作,连接信息: 用户名={}, 连接字符串={}", username, jdbcUrl); } return method.invoke(targetConn, args); } // 生成代理Connection的静态方法 public static Connection wrap(Connection conn, String username, String jdbcUrl) { return (Connection) Proxy.newProxyInstance( Connection.class.getClassLoader(), new Class[]{Connection.class}, new ConnectionLoggingProxy(conn, username, jdbcUrl) ); } }
- 在数据源获取连接的逻辑中,用该代理类包装原始Connection即可生效。
3. MyBatis 拦截器方案
利用MyBatis的拦截器机制,在SQL执行前后记录数据源信息:
- 实现
Interceptor接口,拦截Executor的执行方法 - 示例代码:
@Intercepts({ @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}), @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}) }) public class DataSourceLoggingInterceptor implements Interceptor { private static final Logger log = LoggerFactory.getLogger(DataSourceLoggingInterceptor.class); @Override public Object intercept(Invocation invocation) throws Throwable { // 从上下文获取当前数据源(需和你的数据源上下文实现匹配) DataSource currentDs = DataSourceContextHolder.getCurrentDataSource(); if (currentDs instanceof HikariDataSource) { HikariDataSource hikariDs = (HikariDataSource) currentDs; log.debug("MyBatis执行SQL,数据源信息: 用户名={}, 连接字符串={}", hikariDs.getUsername(), hikariDs.getJdbcUrl().replaceAll("password=.*?&", "password=***&")); } return invocation.proceed(); } }
- 在MyBatis配置类中注册该拦截器即可生效。
关键注意事项
- 敏感信息脱敏:连接字符串通常包含密码,必须替换为
***等脱敏字符,禁止在生产环境打印完整敏感信息 - 性能控制:日志打印会带来性能开销,建议仅在调试阶段开启,或通过日志级别(如DEBUG)控制输出
- 部分请求过滤:若只需记录特定请求,可结合请求上下文(如请求头、参数)添加判断逻辑,符合条件时再打印日志
内容的提问来源于stack exchange,提问作者Aspiring Dev
相关产品推荐
相关产品推荐

