You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 02:05:22