Zipkin原理学习--日志追踪 MySQL 执行语句

目前Zipkin官方提供了插件用于支持对MySQL语句执行过程的日志追踪,提供了对MySQL5、MySQL6和MySQL8的支持,官方地址:https://github.com/openzipkin/brave/tree/master/instrumentation

一、介绍及示例
配置示例:

1、引入相关jar包:

<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-mysql</artifactId>
<version>5.4.3</version>
</dependency>
2、在url中添加拦截器和服务名:statementInterceptors=brave.mysql.TracingStatementInterceptor&zipkinServiceName=myDatabaseService

public void mysql() throws Exception{
Class.forName("com.mysql.jdbc.Driver");
System.out.println("成功加载驱动");

Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;

try {
String url = "jdbc:mysql://localhost:3306/db?user=root&password=root&useUnicode=true&characterEncoding=UTF8&statementInterceptors=brave.mysql.TracingStatementInterceptor&zipkinServiceName=myDatabaseService";
connection = DriverManager.getConnection(url);
System.out.println("成功获取连接");

statement = connection.createStatement();
String sql = "select * from tbl_user";
resultSet = statement.executeQuery(sql);

resultSet.beforeFirst();
while (resultSet.next()) {
System.out.println(resultSet.getString(1));
}
System.out.println("成功操作数据库");
} catch(Throwable t) {
// TODO 处理异常
t.printStackTrace();
} finally {
if (resultSet != null) {
resultSet.close();
}
if (statement != null) {
statement.close();
}
if (connection != null) {
connection.close();
}
System.out.println("成功关闭资源");
}

}
3、zipserver中结果示例:

二、实现原理
        其实现原理也还是挺容易理解的,利用MySQL JDBC提供的拦截器机制,在sql语句执行前新建一个span调用,在sql语句执行后结束span调用,记录整个调用过程的耗时及sql语句信息。

public class TracingStatementInterceptor implements StatementInterceptorV2 {

/**
* Uses {@link ThreadLocalSpan} as there‘s no attribute namespace shared between callbacks, but
* all callbacks happen on the same thread.
*
* <p>Uses {@link ThreadLocalSpan#CURRENT_TRACER} and this interceptor initializes before
* tracing.
*/
@Override
public ResultSetInternalMethods preProcess(String sql, Statement interceptedStatement,
Connection connection) {
// Gets the next span (and places it in scope) so code between here and postProcess can read it
//新生成一个Span
Span span = ThreadLocalSpan.CURRENT_TRACER.next();
if (span == null || span.isNoop()) return null;

// When running a prepared statement, sql will be null and we must fetch the sql from the statement itself
if (interceptedStatement instanceof PreparedStatement) {
sql = ((PreparedStatement) interceptedStatement).getPreparedSql();
}
int spaceIndex = sql.indexOf(‘ ‘); // Allow span names of single-word statements like COMMIT
span.kind(Span.Kind.CLIENT).name(spaceIndex == -1 ? sql : sql.substring(0, spaceIndex));
span.tag("sql.query", sql);
parseServerIpAndPort(connection, span);
//记录启动时间
span.start();
return null;
}

@Override
public ResultSetInternalMethods postProcess(String sql, Statement interceptedStatement,
ResultSetInternalMethods originalResultSet, Connection connection, int warningCount,
boolean noIndexUsed, boolean noGoodIndexUsed, SQLException statementException) {
Span span = ThreadLocalSpan.CURRENT_TRACER.remove();
if (span == null || span.isNoop()) return null;

if (statementException != null) {
span.tag("error", Integer.toString(statementException.getErrorCode()));
}
//记录服务停止时间
span.finish();

return null;
}

/**
* MySQL exposes the host connecting to, but not the port. This attempts to get the port from the
* JDBC URL. Ex. 5555 from {@code jdbc:mysql://localhost:5555/database}, or 3306 if absent.
*/
static void parseServerIpAndPort(Connection connection, Span span) {
try {
URI url = URI.create(connection.getMetaData().getURL().substring(5)); // strip "jdbc:"
String remoteServiceName = connection.getProperties().getProperty("zipkinServiceName");
if (remoteServiceName == null || "".equals(remoteServiceName)) {
String databaseName = connection.getCatalog();
if (databaseName != null && !databaseName.isEmpty()) {
remoteServiceName = "mysql-" + databaseName;
} else {
remoteServiceName = "mysql";
}
}
//添加服务名
span.remoteServiceName(remoteServiceName);
String host = connection.getHost();
if (host != null) {
span.remoteIpAndPort(host, url.getPort() == -1 ? 3306 : url.getPort());
}
} catch (Exception e) {
// remote address is optional
}
}

@Override public boolean executeTopLevelOnly() {
return true; // True means that we don‘t get notified about queries that other interceptors issue
}

@Override public void init(Connection conn, Properties props) {
// Don‘t care
}

@Override public void destroy() {
// Don‘t care
}
}
思考:其实Zipkin官方给出的这种方案还是能给我们一些启发的,目前对于数据库官方只支持了mysql,对于Postgresql、Oracle 和 SQL Server 等可以基于技术方案有以下两种局限解决方案:

(1)利用mybatis的拦截器机制来实现,和上面的实现类似

(2)利用数据库池 Druid的过滤器同样可以实现。

以上两种方案的好处:对于数据库通用支持。
————————————————
原文链接:https://blog.csdn.net/qq924862077/article/details/88559499

原文地址:https://www.cnblogs.com/xd502djj/p/12219798.html

时间: 2024-10-08 17:20:07

Zipkin原理学习--日志追踪 MySQL 执行语句的相关文章

ZipKin原理学习--zipkin支持日志打印追踪信息

   目前在zipkin brave已经提供功能在我们使用logback或log4j等时可以在日志信息中将traceId和spanId等信息打印到运行日志,这样可能对我们通过日志查看解决问题有比较大的帮助. 示例: pom.xml中添加zipkin相关jar <dependency> <groupId>io.zipkin.brave</groupId> <artifactId>brave-instrumentation-spring-web</arti

MySQL执行语句日志分析工具

由于项目更新了一大版本,过不了多久就会进行一次SQL语句查询的优化任务,由运维部分导出一份历史查询SQL语句日志给开发部门做优化分析工作.以前写过一个日志分析的工具,可以不是很好,所以最近又花了几天时间,完善了这个工具.下面,我来说一下,开发历程: 一.任务分析: 1.工具界面: 2.具体需求:分类统计.忽略参数.调试错误 3.预想效果:参数部分用 ? 代替,工具效果展示: 二.开发编码:(读取(bgWorke),写入) 1.变量设置: static string resultFile;//日志

mysql 执行语句

连接数据库: $con = mysql_connect(服务器地址,用户名,密码): 选择数据库: $select = mysql_select_db(数据库名称); $select = mysql_select_db('user'); 关闭数据库: mysql_close (连接数据库的变量): $conn = mysql_connect(localhost,root,root); mysql_close($conn); 执行一条mysql查询: mysql_query(mysql语句); 插

MySql 执行语句错误 Err] 1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near

关于用Power Designer 生成sql文件出现 错误  [Err] 1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'int not null auto_increment comment '用户id', 的解决办法 sql语句如下 1 drop table

yii 显示mysql执行语句

访问protected/config/main.php修改如下 'db'=>array(   'connectionString' => 'mysql:host=localhost;dbname=yii_computer',   'emulatePrepare' => true,   'username' => 'root',   'password' => '',   'charset' => 'utf8',   'tablePrefix' => 'yii_co

EF链接mysql 和mysql 执行语句 监控

Install-Package EntityFramework -Version 6.0.0 Install-Package EntityFramework.zh-Hans -Version 6.0.0 将这个添加引用 C:\Program Files (x86)\MySQL\MySQL Connector Net 6.8.3\Assemblies\v4.5//Install-Package MySql.Data.Entity.EF6 <provider invariantName="My

Oracle OCP 官方PPT学习日志 使用 SQL SELECT 语句检索数据 01

连接运算符,|| 注:也可以将日期表达式连接到其他表达式或列. 文字字符串 SELECT last_name ||' is a '||job_id AS "Employee Details" FROM employees; 其它引号(q)运算符 许多SQL语句都在表达式或条件中使用字符文字.如果文字本身包含一个单引号,则可以使用引号(q)运算符并选择自己的引号分隔符. 可以选择所需要的任何分隔符,单字节或多字节分隔符,或者下列字符对中的任何一种:[ ].{ }.( )或< >

Mysql常用语句总结

对MySql经常使用语句的详细总结 下面总结的知识点全是经常用的,全都是干货,好好收藏吧./* 启动MySQL */ net start mysql /* 连接与断开服务器 */ mysql -h 地址 -P 端口 -u 用户名 -p 密码 /* 跳过权限验证登录MySQL */ mysqld --skip-grant-tables -- 修改root密码 密码加密函数password() update mysql.user set password=password('root'); SHOW

MySql操作语句集锦

Windows服务 -- 启动MySQL    net start mysql-- 创建Windows服务    sc create mysql binPath= mysqld_bin_path(注意:等号与值之间有空格) 连接与断开服务器 mysql -h 地址 -P 端口 -u 用户名 -p 密码 SHOW PROCESSLIST -- 显示哪些线程正在运行SHOW VARIABLES -- 显示系统变量信息 数据库操作 -- 查看当前数据库    SELECT DATABASE();--