前言
很多的小伙伴开发过程中可能都遭遇过打印执行时间的问题。常规的操作就是在需要监控的方法编写几行代码来打印执行时间,勤劳点的小伙伴就会编写aop来打印执行时间,但是绝大部分时候,我们是不需要将这段代码带进生产环境的,所以在上线都要找到相关的代码进行删除。本文提供了一套Java探针程序,可以在开发或者测试(压测)阶段协助调试代码。同时提供生成traceId,方便追踪方法链路。
先提供源码地址:
https://github.com/Jamsw/bytebuddy-agent.git
提示:以下是本篇文章正文内容,下面案例可供参考
一、byte-buddy是什么?
Byte Buddy是一个JVM的运行时代码生成器,提供了简单的API,便于手工、通过Java Agent,或者在构建期间修改字节码。
二、使用步骤
1.编译成jar包
命令如下:
mvn clean package
执行完成后,会在target目录中生成jar包以及config目录。jar包和config需要在相同目录下。如下图所示:
config目录下包含agent.config配置文件,其中包含两个参数:
agent.packname = com.smart.tcp.gateway
agent.ignore = com.smart.tcp.gateway.client.test.UserModel,com.smart.tcp.gateway.client.test.UserModel
agent.packname:需要打印日志的包路径。
agent.ignore:需要忽略的类。
2.配置Java探针
拿idea开发工具举例,需要在VM options配置如下参数:
-javaagent:path(填写本机或者服务器目录地址)/log-agent-1.0-SNAPSHOT.jar
3.效果演示
tid:71b8195c-eadc-4385-8db1-a8fb414743e8:INFO>方法级别【类名:com.smart.tcp.gateway.client.test.UserMapper 方法名:<init> 执行时间为:0ms】
tid:71b8195c-eadc-4385-8db1-a8fb414743e8:INFO>方法级别【类名:com.smart.tcp.gateway.client.test.UserMapper 方法名:selectUserInfo 执行时间为:0ms】
tid:71b8195c-eadc-4385-8db1-a8fb414743e8:INFO>方法级别【类名:com.smart.tcp.gateway.client.test.UserServiceImpl 方法名:getUserInfo 执行时间为:10ms】
tid:71b8195c-eadc-4385-8db1-a8fb414743e8:INFO>接口级别【url path:/user/getInfo 入参:key=userId value=123456,出参:{"userName":"smart","userId":null} 接口整体耗时:109ms】
三、遭遇问题
1.打桩
由于要打印接口返回值,所以需要将HttpServletResponse进行二次封装,agent程序需要将二次封装的reponse传给拦截的方法,所以需要使用打桩。
首先重写superCall方法,如下:
public interface OverrideCallable {
Object call(Object[] args);
}
然后在ProcessRequestInterceptor方法入参加入OverrideCallable,如下:
public static Object intercept(@AllArguments Object[] allArguments, @Morph OverrideCallable zuper)
在premain加入打桩配置.withDefaultConfiguration().withBinders(Morph.Binder.install(OverrideCallable.class)),如下所示:
agentBuilder.ignore(ElementMatchers.named("javax.servlet.http"))
.type(ElementMatchers.named("org.springframework.web.servlet.FrameworkServlet"))
.transform((builder, type, classLoader, module) ->
builder.method(ElementMatchers.named("processRequest"))
.intercept(MethodDelegation.withDefaultConfiguration().withBinders(Morph.Binder.install(OverrideCallable.class)).to(ProcessRequestInterceptor.class)))
.installOn(inst);
2.mybatis报错
由于byte-buddy在注入代码的时候会修改原始类名,所以mybatis通过类名查询方法的时候会报java.lang.NoSuchMethodError报错,解决方式为配置不修改类名.disableClassFormatChanges(), 具体代码如下:
agentBuilder.disableClassFormatChanges().ignore(e)
.type(ElementMatchers.nameStartsWith(Config.Agent.packname))
.transform(transformer).installOn(inst);