背景
公司的一个需求,公司既有的链路追踪日志组件要支持mysql的sql执行时间打印,要实现链路追踪常用的手段就是实现第三方框架或工具提供的拦截器接口或者是过滤器接口,对于mysql也不例外,实际上就是实现了mysql驱动的拦截器接口而已。
具体实现
MySQL的渠道有不同的版本,不同版本的拦截器接口是不同的,所以要针对你所使用的不同版本的MySQL驱动去实现响应的拦截器,接下来分别介绍下MySQL渠道5,6,8版本的实现方式。
MySQL5
这里以MySQL渠道5.1.18版本为例实现,实现StatementInterceptorV2接口,主要实现逻辑在preProcess和postProcess方法,这两个方法是sql执行前后要执行的方法,我所使用的框架是logback,这里使用MDC来记录sql执行前的一个时间戳,代码在postProcess方法MDC.put("sql_exec_time", start);,自己也可以使用ThreadLocal等来实现,然后在postProcess方法中使用MDC.get("sql_exec_time")将记录的sql执行前的时间取出来,最后再用当前时间戳减去sql执行前的时间,就算出了sql执行的时间。
import static net.logstash.logback.marker.Markers.append;import com.mysql.jdbc.Connection;import com.mysql.jdbc.ResultSetInternalMethods;import com.mysql.jdbc.Statement;import com.mysql.jdbc.StatementInterceptorV2;import com.redick.util.LogUtil;import java.sql.SQLException;import java.util.Properties;import lombok.extern.slf4j.Slf4j;import org.slf4j.MDC;/** * @author Redick01 */@Slf4jpublic class Mysql5StatementInterceptor implements StatementInterceptorV2 { @Override public void init(Connection connection, Properties properties) throws SQLException { } @Override public ResultSetInternalMethods preProcess(String s, Statement statement, Connection connection) throws SQLException { String start = String.valueOf(System.currentTimeMillis()); MDC.put("sql_exec_time", start); log.info(LogUtil.customizeMarker(LogUtil.kLOG_KEY_TRACE_TAG, "sql_exec_before"), "开始执行sql"); return null; } @Override public boolean executeTopLevelOnly() { return false; } @Override public void destroy() { } @Override public ResultSetInternalMethods postProcess(String s, Statement statement, ResultSetInternalMethods resultSetInternalMethods, Connection connection, int i, boolean b, boolean b1, SQLException e) throws SQLException { long start = Long.parseLong(MDC.get("sql_exec_time")); long end = System.currentTimeMillis(); log.info(LogUtil.customizeMarker(LogUtil.kLOG_KEY_TRACE_TAG, "sql_exec_after") .and(append(LogUtil.kLOG_KEY_SQL_EXEC_DURATION, end - start)), "结束执行sql"); return null; }}
MySQL6
MySQL6和MySQL5基本一样,只是接口不是同一个,直接放代码
import static net.logstash.logback.marker.Markers.append;import com.mysql.cj.api.MysqlConnection;import com.mysql.cj.api.jdbc.Statement;import com.mysql.cj.api.jdbc.interceptors.StatementInterceptor;import com.mysql.cj.api.log.Log;import com.mysql.cj.api.mysqla.result.Resultset;import com.redick.util.LogUtil;import java.sql.SQLException;import java.util.Properties;import lombok.extern.slf4j.Slf4j;import org.slf4j.MDC;/** * @author Redick01 */@Slf4jpublic class Mysql6StatementInterceptor implements StatementInterceptor { @Override public StatementInterceptor init(MysqlConnection mysqlConnection, Properties properties, Log log) { return null; } @Override public T preProcess(String s, Statement statement) throws SQLException { String start = String.valueOf(System.currentTimeMillis()); MDC.put("sql_exec_time", start); log.info(LogUtil.customizeMarker(LogUtil.kLOG_KEY_TRACE_TAG, "sql_exec_before"), "开始执行sql"); return null; } @Override public boolean executeTopLevelOnly() { return false; } @Override public void destroy() { } @Override public T postProcess(String s, Statement statement, T t, int i, boolean b, boolean b1, Exception e) throws SQLException { long start = Long.parseLong(MDC.get("sql_exec_time")); long end = System.currentTimeMillis(); log.info(LogUtil.customizeMarker(LogUtil.kLOG_KEY_TRACE_TAG, "sql_exec_after") .and(append(LogUtil.kLOG_KEY_SQL_EXEC_DURATION, end - start)), "结束执行sql"); return null; }}
MySQL8
MySQL8和MySQL5/6的拦截器接口又不一样了,MySQL8的拦截器接口是com.mysql.cj.interceptors.QueryInterceptor,统计sql执行时间的方式还是一样的,代码如下:
立即学习“Java免费学习笔记(深入)”;
Soundful
Soundful Ai音乐生成器,只需一个按钮即可生成免版税曲目
240 查看详情
import static net.logstash.logback.marker.Markers.append;import com.mysql.cj.MysqlConnection;import com.mysql.cj.Query;import com.mysql.cj.interceptors.QueryInterceptor;import com.mysql.cj.log.Log;import com.mysql.cj.protocol.Resultset;import com.mysql.cj.protocol.ServerSession;import com.redick.util.LogUtil;import java.util.Properties;import java.util.function.Supplier;import lombok.extern.slf4j.Slf4j;import org.slf4j.MDC;/** * @author Redick01 */@Slf4jpublic class Mysql8QueryInterceptor implements QueryInterceptor { @Override public QueryInterceptor init(MysqlConnection mysqlConnection, Properties properties, Log log) { return null; } @Override public T preProcess(Supplier supplier, Query query) { String start = String.valueOf(System.currentTimeMillis()); MDC.put("sql_exec_time", start); log.info(LogUtil.customizeMarker(LogUtil.kLOG_KEY_TRACE_TAG, "sql_exec_before"), "开始执行sql"); return null; } @Override public boolean executeTopLevelOnly() { return false; } @Override public void destroy() { } @Override public T postProcess(Supplier supplier, Query query, T t, ServerSession serverSession) { long start = Long.parseLong(MDC.get("sql_exec_time")); long end = System.currentTimeMillis(); log.info(LogUtil.customizeMarker(LogUtil.kLOG_KEY_TRACE_TAG, "sql_exec_after") .and(append(LogUtil.kLOG_KEY_SQL_EXEC_DURATION, end - start)), "结束执行sql"); return null; }}
使用方法
MySQL5和6的使用方式一样,在数据库链接的url中增加如下statementInterceptors参数,例如:
url: jdbc:mysql://127.0.0.1:3316/log-helper?useUnicode=true&characterEncoding=UTF8&statementInterceptors=com.redick.support.mysql.Mysql5StatementInterceptor&serverTimezone=CST
MySQL8则是在url中增加queryInterceptors参数,例如:
url: jdbc:mysql://127.0.0.1:3316/log-helper?useUnicode=true&characterEncoding=UTF8&queryInterceptors=com.redick.support.mysql.Mysql8QueryInterceptor&serverTimezone=CST
测试结果
sql执行前日志
{"@timestamp":"2023-02-28T17:16:29.234+08:00","@version":"0.0.1","message":"开始执行sql","logger_name":"com.redick.support.mysql.Mysql5StatementInterceptor","thread_name":"http-nio-3321-exec-4","level":"INFO","level_value":20000,"traceId":"9ed930dc-4cc6-4719-bf33-9fcb618fd65b","spanId":"1","request_type":"getName","parentId":"0","trace_tag":"sql_exec_before"}
sql执行后日志,sql_duration标识执行sql耗时3ms
{"@timestamp":"2023-02-28T17:16:29.237+08:00","@version":"0.0.1","message":"结束执行sql","logger_name":"com.redick.support.mysql.Mysql5StatementInterceptor","thread_name":"http-nio-3321-exec-4","level":"INFO","level_value":20000,"traceId":"9ed930dc-4cc6-4719-bf33-9fcb618fd65b","spanId":"1","request_type":"getName","parentId":"0","trace_tag":"sql_exec_after","sql_duration":3}
以上就是java怎么通过MySQL驱动拦截器实现执行sql耗时计算的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/758974.html
微信扫一扫
支付宝扫一扫