Druid LogFilter输出可执行的SQL

news2024/10/2 14:37:41

配置

测试代码:

DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("xxx");
dataSource.setUsername("xxx");
dataSource.setPassword("xxx");
dataSource.setFilters("slf4j");
dataSource.setValidationQuery("SELECT 1");
dataSource.setTestOnBorrow(true);
dataSource.setTestWhileIdle(true);

Connection connection = dataSource.getConnection();

PreparedStatement stmt = connection.prepareStatement("select * from tb_order where id=?");
stmt.setInt(1,1);
stmt.execute();

在配置输出可执行的SQL之前,看下Druid的日志输出:

SQL占位符和参数分开打印

配置输出可执行的SQL

Java启动参数配置方式

-Ddruid.log.stmt.executableSql=true

logFilter参数直接配置:

<bean id="log-filter" class="com.alibaba.druid.filter.logging.Log4jFilter">
        <property name="statementExecutableSqlLogEnable" value="true" />
  </bean>

在配置输出可执行的SQL之后,看下Druid的日志输出:

打印出了可执行的SQL

源码

调用栈是DruidPooledPreparedStatement.execute->StatementProxyImpl.execute-> FilterChainImpl.preparedStatement_execute->FilterEventAdapter.preparedStatement_execute(所有Filter都继承了FilterEventAdapter)-> LogFilter.statementExecuteAfter->LogFilter.logExecutableSql。源码如下:

// DruidPooledPreparedStatement.execute
public boolean execute() throws SQLException {
    checkOpen();

    incrementExecuteCount();
    transactionRecord(sql);

    oracleSetRowPrefetch();

    conn.beforeExecute();
    try {
        //这个stmt是一个StatementProxyImpl实例
        return stmt.execute();
    } catch (Throwable t) {
        errorCheck(t);

        throw checkException(t);
    } finally {
        conn.afterExecute();
    }
}

StatementProxyImpl.execute:

// StatementProxyImpl.execute
public boolean execute() throws SQLException {
    updateCount = null;
    lastExecuteSql = sql;
    lastExecuteType = StatementExecuteType.Execute;
    lastExecuteStartNano = -1L;
    lastExecuteTimeNano = -1L;
    //调用过滤器链的preparedStatement_execute
    firstResultSet = createChain().preparedStatement_execute(this);
    return firstResultSet;
}

FilterChainImpl.preparedStatement_execute:

public boolean preparedStatement_execute(PreparedStatementProxy statement) throws SQLException {
    if (this.pos < filterSize) {
        // 调用过滤器的preparedStatement_execute方法
        return nextFilter().preparedStatement_execute(this, statement);
    }
    return statement.getRawObject().execute();
}

所有过滤器都继承了FilterEventAdapter,看名字是个和事件有关的类,这个父类里面实现了preparedStatement_execute方法:

// FilterEventAdapter.preparedStatement_execute
public boolean preparedStatement_execute(FilterChain chain, PreparedStatementProxy statement) throws SQLException {
    try {
        statementExecuteBefore(statement, statement.getSql());
        // 递归调用配置的所有过滤器的preparedStatement_execute方法,直到真正执行完statement
        boolean firstResult = chain.preparedStatement_execute(statement);
        // 在执行完statement后执行,这是个空方法,具体的逻辑在子类里面实现,即LogFilter
        this.statementExecuteAfter(statement, statement.getSql(), firstResult);

        return firstResult;

    } catch (SQLException error) {
        statement_executeErrorAfter(statement, statement.getSql(), error);
        throw error;
    } catch (RuntimeException error) {
        statement_executeErrorAfter(statement, statement.getSql(), error);
        throw error;
    } catch (Error error) {
        statement_executeErrorAfter(statement, statement.getSql(), error);
        throw error;
    }

}

LogFilter.statementExecuteAfter方法:

protected void statementExecuteAfter(StatementProxy statement, String sql, boolean firstResult) {
    // 打印可执行的SQL
    logExecutableSql(statement, sql);
    // 统计SQL执行的时间
    if (statementExecuteAfterLogEnable && isStatementLogEnabled()) {
        statement.setLastExecuteTimeNano();
        double nanos = statement.getLastExecuteTimeNano();
        double millis = nanos / (1000 * 1000);

        statementLog("{conn-" + statement.getConnectionProxy().getId() + ", " + stmtId(statement) + "} executed. "
                     + millis + " millis. " + sql);
    }
}

LogFilter.logExecutableSql:

private void logExecutableSql(StatementProxy statement, String sql) {
    // 没有配置输出可执行SQL,直接返回
    if ((!isStatementExecutableSqlLogEnable()) || !isStatementLogEnabled()) {
        return;
    }
    // 获取SQL参数数量
    int parametersSize = statement.getParametersSize();
    if (parametersSize == 0) {
        statementLog("{conn-" + statement.getConnectionProxy().getId() + ", " + stmtId(statement) + "} executed. "
                     + sql);
        return;
    }
    // 获取SQL的参数,存到parameters中
    List<Object> parameters = new ArrayList<Object>(parametersSize);
    for (int i = 0; i < parametersSize; ++i) {
        JdbcParameter jdbcParam = statement.getParameter(i);
        parameters.add(jdbcParam != null
                ? jdbcParam.getValue()
                : null);
    }

    DbType dbType = DbType.of(statement.getConnectionProxy().getDirectDataSource().getDbType());
    // 最终由SQLUtils根据参数列表和预执行SQL,转换为可执行SQL
    String formattedSql = SQLUtils.format(sql, dbType, parameters, this.statementSqlFormatOption);
    // statementLog是个抽象方法,由具体的日志过滤器类实现(如Slf4jLogFilter)
    statementLog("{conn-" + statement.getConnectionProxy().getId() + ", " + stmtId(statement) + "} executed. "
                 + formattedSql);
}
    

总结

输出可执行SQL在日常排查SQL执行错误还是很实用的。其原理是在PreparedStatement.execute执行之后,调用SQLUtils.format打印出可执行的SQL。FilterEventAdapter这个类很关键,它会在SQL执行之前或者之后,调用扩展的处理,具体的处理逻辑又委派给子类实现。

Springboot Druid配置可执行sql配置_druid 执行sql_孙陆泉的博客-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/983262.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023年大数据平台数据安全厂商汇总

大数据时代&#xff0c;大数据平台数据安全至关重要&#xff0c;这关系着大家的切身安全。所以企业一定要慎重选择大数据平台数据安全厂商。这里给大家简单汇总一下&#xff0c;同时给大家推荐一下&#xff0c;仅供参考哦&#xff01; 2023年大数据平台数据安全厂商汇总 1、…

Kafka3.0.0版本——消费者(消费方式)

目录 一、Kafka 消费方式1.1、pull&#xff08;拉&#xff09; 模式1.2、push &#xff08;推&#xff09;模式1.3、Kafka采用pull&#xff08;拉&#xff09; 模式缺点 一、Kafka 消费方式 1.1、pull&#xff08;拉&#xff09; 模式 consumer采用从broker中主动拉取数据。K…

【Python小项目之Tkinter应用】随机点名/抽奖工具大优化:实现背景图与其他组件自适应窗口大小变化并保持相对位置和比例不变

文章目录 前言一、需求分析与实现思路明确需求实现思路二、关键代码2.1 实现背景图随着窗口大小变化而变化2.2 更换place的参数三、完整代码四、总结4.1 意外收获前言 话不多说,直接看优化后的效果: 优化前: 是不是非常的哇塞,相比于之前只能固定窗口大小来运行,优化后…

基于SSM的营业厅宽带系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

大数据课程L1——网站流量项目的概述整体架构

文章作者邮箱:yugongshiye@sina.cn 地址:广东惠州 ▲ 本章节目的 ⚪ 了解网站流量项目的案例概述; ⚪ 了解网站流量项目的数据埋点和采集; ⚪ 了解网站流量项目的整体架构; 一、网站流量项目概述 1. 背景说明 网站流量统计是改进网站服务的重要手段之一…

大厂常见面试题LRU算法实现

要实现put和get的O(1)时间复杂度 最近最少/最久使用问题 将最近最少使用的数据淘汰 LRU缓存是操作系统常见的页面缓存淘汰机制 实现方式&#xff1a;哈希表 双向链表 哈希表用于存储数据&#xff0c;主要用于实现存取操作put和get的O(1)时间复杂度双向链表用于记录最近最少…

Linux系统访问卡顿 慢

Linux系统访问卡顿、慢 1、查看磁盘使用情况2、查看内存使用情况3、查看磁盘IO使用情况4、查看CPU使用情况5、iostat的查询 1、查看磁盘使用情况 [rootlocalhost ~]# df -h 当发现磁盘使用率很高时&#xff0c;那就要释放磁盘空间了&#xff0c;删除一些不必要的文件查看各个目…

03 Linux

第一课 目录 ⼀、进程与线程 1. 进程 2. 线程 3. 进程和线程的使用条件 ⼆、linux下的sudo命令 (1)sudo sudo的功能&#xff1f; sudo提供的临时权限什么时候被取消&#xff1f; (2)为什么要取消 sudo 权限呢&#xff1f; (2)apt sudo apt update 三、linux常⽤…

第10篇:ESP32外部中断功能的使用

第1篇:Arduino与ESP32开发板的安装方法 第2篇:ESP32 helloword第一个程序示范点亮板载LED 第3篇:vscode搭建esp32 arduino开发环境 第4篇:vscodeplatformio搭建esp32 arduino开发环境 ​​​​​​第5篇:doit_esp32_devkit_v1使用pmw呼吸灯实验 第6篇:ESP32连接无源喇叭播…

BEVFormer论文解析

背景 跟基于雷达的相比&#xff0c;纯视觉的方案成本低&#xff0c;而且具有RGB和纹理信息&#xff0c;可以用于检测像交通信号灯、车道线等道路要素。 自动驾驶的感知本意是要从多个摄像头给出的2D信息来预测3D bbox(检测)或者semantic map(分割)&#xff0c;最直接的方案是…

PlantUML——类图(持续更新)

前言 在分析代码流程中&#xff0c;我们常常会使用到各种UML图&#xff0c;例如用例图、时序图和类图等&#xff0c;以往使用ProcessOn或亿图图示等工具&#xff0c;但是这些工具难以规范化&#xff0c;有没有一种用代码来生成图形的工具呢&#xff1f; 刚好在出差的晨会中机缘…

神策分析 2.5 上线「经营分析」:可视化分析能力重磅升级!

近日&#xff0c;神策数据发布了全新的神策分析 2.5 版本&#xff0c;该版本以经营分析为主题&#xff0c;支持分析模型与外部数据的融合性接入&#xff0c;构建全域数据融合模型&#xff0c;并提供统一、便捷的指标及可视化能力&#xff0c;实现从用户到经营的全链路、全场景分…

【公网远程手机Android服务器】安卓Termux搭建Web服务器

目录 概述 1.搭建apache 2.安装cpolar内网穿透 3.公网访问配置 4.固定公网地址 5.添加站点 概述 Termux是一个Android终端仿真应用程序&#xff0c;用于在 Android 手机上搭建一个完整的Linux 环境&#xff0c;能够实现Linux下的许多基本操作&#xff0c;不需要root权限…

归并排序 Merge Sort

归并排序的基本思想是什么&#xff1f; 采用分治法&#xff08;Divide and Conquer&#xff09;&#xff0c;递归将待排序的数组平均分成两个子数组&#xff0c;直到子数组长度为 0 或 1&#xff0c;这是 分 Divider。再将排序好的两个子数组合并为一个有序数组&#xff0c;这…

AD学习笔记

1快捷键 A 各种对齐 L 切换顶层底层 右键联合 元器件的锁定 N 连线控制是否显示 2原理图和pcb交叉选择 封装重叠警告 在规则中修改

Tcp协议特点

目录 确认应答&#xff08;ACK&#xff09; 超时重传 建立连接-三次握手 断开连接-四次挥手 Tcp特点主要是分为四点&#xff1a;有连接&#xff0c;可靠传输&#xff0c;面向字节流&#xff0c;全双工。 这里我们主要介绍可靠传输 确认应答&#xff08;ACK&#xff09; …

EasyFalsh移植使用方法

参考&#xff1a;https://blog.csdn.net/Mculover666/article/details/105510837 注意&#xff1a; 这里说的修改默认环境变量后修改环境变量版本号就自动重新写入到flash这句话是有问题的&#xff0c;要开启上面【#define EF_ENV_AUTO_UODATE】宏定义后才会实现该功能&#…

Maven 必备技能:MAC 系统下 JDK和Maven 安装及环境变量配置详细讲解

开发中难免因系统问题或者版本变更反复折腾JDK和Maven环境变量&#xff0c;干脆写个笔记备忘个&#xff0c;也方便小伙伴们节省时间。 JDK安装与环境变量配置 1.官网下载jdk mac安装包: Java Downloads | Oracle " https://www.oracle.com/java/technologies/downloads…

携程网关的架构设计-日流量200亿

一、概述 类似于许多企业的做法&#xff0c;携程 API 网关是伴随着微服务架构一同引入的基础设施&#xff0c;其最初版本于 2014 年发布。随着服务化在公司内的迅速推进&#xff0c;网关逐步成为应用程序暴露在外网的标准解决方案。后续的“ALL IN 无线”、国际化、异地多活等…

【爬虫笔记】Python爬虫简单运用爬取代理IP

一、前言 近些年来&#xff0c;网络上的爬虫越来越多&#xff0c;很多网站都针对爬虫进行了限制&#xff0c;封禁了一些不规则的请求。为了实现正常的网络爬虫任务&#xff0c;爬虫常用代理IP来隐藏自己的真实IP&#xff0c;避免被服务器封禁。本文将介绍如何使用Python爬虫来…