Druid LogFilter输出可执行的SQL

配置

测试代码:

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_executefirstResultSet = 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方法,直到真正执行完statementboolean firstResult = chain.preparedStatement_execute(statement);// 在执行完statement后执行,这是个空方法,具体的逻辑在子类里面实现,即LogFilterthis.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) {// 打印可执行的SQLlogExecutableSql(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,转换为可执行SQLString 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.hqwc.cn/news/102119.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java“牵手”淘宝商品列表数据,关键词搜索淘宝商品数据接口,淘宝API申请指南

淘宝商城是一个网上购物平台&#xff0c;售卖各类商品&#xff0c;包括服装、鞋类、家居用品、美妆产品、电子产品等。要获取淘宝商品列表和商品详情页面数据&#xff0c;您可以通过开放平台的接口或者直接访问淘宝商城的网页来获取商品详情信息。以下是两种常用方法的介绍&…

大模型技术实践(三)|用LangChain和Llama 2打造心灵疗愈机器人

上期文章我们实现了Llama 2-chat-7B模型的云端部署和推理&#xff0c;本期文章我们将用“LangChainLlama 2”的架构打造一个定制化的心灵疗愈机器人。有相关知识背景的读者可以直接阅读「实战」部分。 01 背景 1.1 微调 vs. 知识库 由于大模型在垂直行业领域的问答效果仍有待提…

Revit SDK:Selections 选择

前言 Revit 作为一款成熟的商业软件&#xff0c;它将自己的UI选择功能也通过 API 暴露出来。通过 API 可以按照特定的过滤规则来选择相应的元素&#xff0c;能力和UI基本上是等价的。这个 SDK 用四个例子展示了 API 的能力&#xff0c;内容如下。 内容 PickforDeletion 核心…

A133P EC200M模块调试

Linux USB驱动框架&#xff1a; USB 是一种分层总线结构。USB 设备与主机之间的数据传输由 USB 控制器控制。Linux USB 驱动程序架构如下图所示。Linux USB 主机驱动包括三部分&#xff1a;USB 主机控制器驱动、USB 核心和 USB 设备驱动。 模块加载 USB 转串口 option 驱动程序…

记录docker 部署nessus

1、开启容器 docker run -itd --nameramisec_nessus -p 8834:8834 ramisec/nessus 2、登录 &#xff1a;注意是https https://ip8843 3、修改admin密码 #进入容器 docker exec -it ramisec_nessus /bin/bash#列出用户名 /opt/nessus/sbin/nessuscli lsuser#修改密码&a…

JAVA设计模式第十讲:SPI - 业务差异解决方案

JAVA设计模式第十讲&#xff1a;SPI - 业务差异解决方案 我们需要在不修改源代码的情况下&#xff0c;动态为程序提供一系列额外的特性。首先想到的是Spring的AOP技术来构建应用插件&#xff0c;但是在Java自带的插件中&#xff0c;就有完整的实现。SPI&#xff08;Service Pro…

echarts饼图label自定义样式

生成的options {"tooltip": {"trigger": "item","axisPointer": {"type": "shadow"},"backgroundColor": "rgba(9, 24, 48, 0.5)","borderColor": "rgba(255,255,255,0.4)&q…

DQN算法概述及基于Pytorch的DQN迷宫实战代码

一. DQN算法概述 1.1 算法定义 Q-Learing是在一个表格中存储动作对应的奖励值&#xff0c;即状态-价值函数Q(s,a)&#xff0c;这种算法存在很大的局限性。在现实中很多情况下&#xff0c;强化学习任务所面临的状态空间是连续的&#xff0c;存在无穷多个状态&#xff0c;这种情…

CSS笔记(黑马程序员pink老师前端)浮动,清除浮动

浮动可以改变标签的默认排列方式。浮动元素常与标准流的父元素搭配使用. 网页布局第一准则:多个块级元素纵向排列找标准流&#xff0c;多个块级元素横向排列找浮动。 float属性用于创建浮动框&#xff0c;将其移动到一边&#xff0c;直到左边缘或右边缘触及包含块或另一个浮动框…

Xilinx IDDR与ODDR原语的使用

文章目录 ODDR原语1. OPPOSITE_EDGE 模式2. SAME_EDGE 模式 ODDR原语 例化模板&#xff1a; ODDR #(.DDR_CLK_EDGE("OPPOSITE_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE" .INIT(1b0), // Initial value of Q: 1b0 or 1b1.SRTYPE("SYNC…

使用Python操作MySQL数据库

准备 安装Python,打开命令提示符&#xff0c;我已经安装成功了 安装Mysql我也安装成功了 我在用户的86188下利用记事本写了一个.py的python代码&#xff0c;在命令提示符中运行 Python自带的集成式开发环境,在电脑搜索框直接IDEA你也会打开 一&#xff0c;建立连接 使用Python…

colab使用(基础入门)——随手记

挂载到google drive 挂载目录/content/drive from google.colab import drive drive.mount(/content/drive) 图解colab读取Google Drive 文件 - 知乎 下载文件 !curl -L https://dl.fbaipublicfiles.com/imagebind/imagebind_huge.pth -o imagebind_ckpt参数&#xff1a;[-…