mybatis sql 打印
场景
在开发环境,需要实时查看 mybatis 打印的完整sql ,也可以使用插件手动复制转换,比较麻烦,现在尝试通过 java agent方式进行拦截打印完整 sql , 字节码技术 bytebuddy, 当然也可以使用javaassist
实现
目前支持 mysql 驱动 8.x
引入依赖
<properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><bytebuddy.version>1.14.18</bytebuddy.version><logback.version>1.5.6</logback.version></properties><dependencies><!--sql 格式化--><dependency><groupId>com.github.vertical-blank</groupId><artifactId>sql-formatter</artifactId><version>2.0.5</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.34</version><optional>true</optional></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><dependency><groupId>net.bytebuddy</groupId><artifactId>byte-buddy</artifactId><version>${bytebuddy.version}</version></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><version>${mysql.version}</version><optional>true</optional></dependency><dependency><groupId>net.bytebuddy</groupId><artifactId>byte-buddy-agent</artifactId><version>${bytebuddy.version}</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>2.0.13</version><optional>true</optional></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-core</artifactId><version>${logback.version}</version><optional>true</optional></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>${logback.version}</version><optional>true</optional></dependency></dependencies><build><finalName>mybatis-log</finalName><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-shade-plugin</artifactId><version>3.2.4</version><executions><execution><phase>package</phase><goals><goal>shade</goal></goals><configuration><createDependencyReducedPom>false</createDependencyReducedPom><transformers><transformerimplementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"><manifestEntries><!-- 修改为正确的Premain地址 --><Premain-Class>org.example.sample.log.SqlLogAgent</Premain-Class></manifestEntries></transformer></transformers><filters><filter><artifact>*:*</artifact><excludes><exclude>META-INF/*.SF</exclude><exclude>META-INF/*.DSA</exclude><exclude>META-INF/*.RSA</exclude></excludes></filter></filters></configuration></execution></executions></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>1.8</source><target>1.8</target></configuration></plugin></plugins></build>
SqlLogAgent 定义入口
publicclassSqlLogAgent{publicstaticvoidpremain(String agentArgs,Instrumentation inst){//目前 mysql 8.x//拦截 ClientPreparedQuerynewAgentBuilder.Default().type(ElementMatchers.named("com.mysql.cj.ClientPreparedQuery")).transform((builder, typeDescription, classLoader,module,protectionDomain)->
builder.method(ElementMatchers.named("fillSendPacket"))//增加逻辑委托给 MysqlCJLoggerInterceptor.intercept(MethodDelegation.to(MysqlCJLoggerInterceptor.class))).installOn(inst);}}
MysqlCJLoggerInterceptor 增强逻辑
@Slf4jpublicclassMysqlCJLoggerInterceptor{/*
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onSetParameterEnter(@Advice.Origin("#t") String methodName) throws SQLException {
log.info("agent enter {}", methodName);
System.out.println("agent enter: "+methodName);
}
@Advice.OnMethodExit(suppress = Throwable.class)
public static void onFillSendPacketExit(@Advice.Origin("#t") String methodName,
@Advice.Return Message message) throws SQLException {
try {
log.info("methodName: {}", methodName);
String s = new String(message.getByteBuffer());
log.info("mysql log: {}", s);
} catch (Exception e) {
log.error("onFillSendPacketEnter error:", e);
}
}*//* @Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(@Advice.Origin MethodDescription method,
@Advice.This Object instance) {
System.out.println("Method " + method.getName() + " entered in " + instance.getClass().getSimpleName());
}
@Advice.OnMethodExit(suppress = Throwable.class)
public static void onExit(@Advice.Origin MethodDescription method,
@Advice.Return(readOnly = false) Object returnValue) {
System.out.println("Method " + method.getName() + " exited in " + method.getDeclaringType().asErasure().getInternalName());
}*///========================以上都没有效果=======================////拦截运行时@RuntimeTypepublicstaticObjectintercept(@OriginMethod method,@SuperCallCallable<?> callable)throwsException{StopWatch stopWatch =newStopWatch();
stopWatch.start();Message call =(Message) callable.call();
stopWatch.stop();String sql =newString(call.getByteBuffer());formatSqlLog(sql.trim(),stopWatch);return call;}/**
* 格式化 sql 日志
* @param sql
* @param stopWatch
*/privatestaticvoidformatSqlLog(String sql,StopWatch stopWatch){String format ="\n--------------------【sql log】---------------------\n\n {} \n\n耗时: {}";
log.info(format,sql,prettyPrint(stopWatch));}/**
* 格式化
*
* @param stopWatch
* @return
*/publicstaticStringprettyPrint(StopWatch stopWatch){StringBuilder sb =newStringBuilder(stopWatch.getTotalTimeMillis()+"ms");
sb.append(" ");
sb.append(stopWatch.getTotalTimeNanos()+"ns");
sb.append('\n');if(stopWatch.getTaskInfo()==null){
sb.append("No task info kept");}else{
sb.append("---------------------------------------------\n");
sb.append("ns % Task name\n");
sb.append("---------------------------------------------\n");NumberFormat nf =NumberFormat.getNumberInstance();
nf.setMinimumIntegerDigits(9);
nf.setGroupingUsed(false);NumberFormat pf =NumberFormat.getPercentInstance();
pf.setMinimumIntegerDigits(3);
pf.setGroupingUsed(false);for(StopWatch.TaskInfo task : stopWatch.getTaskInfo()){
sb.append(nf.format(task.getTimeNanos())).append(" ");
sb.append(pf.format((double) task.getTimeNanos()/ stopWatch.getTotalTimeNanos())).append(" ");
sb.append(task.getTaskName()).append("\n");}}return sb.toString();}}
测试
在项目启动时加入
-javaagent:D:\xx\mybatis-log.jar
, 结果:
--------------------【sql log】---------------------
select address from t_address
WHERE address like concat('%','shanghai','%')
and is_delete =0
耗时: 0ms 393200ns
---------------------------------------------
ns % Task name
---------------------------------------------
000393200 100%
good luck !
版权归原作者 重生之我是一名程序员 所有, 如有侵权,请联系我们删除。