-
Notifications
You must be signed in to change notification settings - Fork 3.8k
2019 07 11 基于JavaAgent的全链路监控二《通过字节码增加监控执行耗时》
作者:小傅哥
博客:https://bugstack.cn - 原创系列专题
沉淀、分享、成长,让自己和他人都能有所收获!
- 基于JavaAgent的全链路监控一《嗨!JavaAgent》
- 基于JavaAgent的全链路监控二《通过字节码增加监控执行耗时》
- 基于JavaAgent的全链路监控三《ByteBuddy操作监控方法字节码》
- 基于JavaAgent的全链路监控四《JVM内存与GC信息》
- 基于JavaAgent的全链路监控五《ThreadLocal链路追踪》
- 基于JavaAgent的全链路监控六《开发应用级监控》
通过上一章节的介绍《嗨!JavaAgent》,我们已经知道通过配置-javaagent:文件.jar后,在java程序启动时候会执行premain方法。接下来我们使用javassist字节码增强的方式,来监控方法程序的执行耗时。
Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的。它已加入了开放源代码JBoss应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态"AOP"框架。
关于java字节码的处理,目前有很多工具,如bcel,asm。不过这些都需要直接跟虚拟机指令打交道。如果你不想了解虚拟机指令,可以采用javassist。javassist是jboss的一个子项目,其主要的优点,在于简单,而且快速。直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。
- IntelliJ IDEA Community Edition
- jdk1.8.0_45 64位
- 配置位置:Run/Debug Configurations -> VM options
- 配置内容:-javaagent:E:\itstack\GIT\itstack.org\itstack-demo-agent\itstack-demo-agent-02\target\itstack-demo-agent-02-1.0.0-SNAPSHOT.jar=testargs
itstack-demo-agent-02
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── org.itstack.demo.agent
│ │ ├── MyAgent.java
│ │ └── MyMonitorTransformer.java
│ └── resources
│ └── META-INF
│ └── MANIFEST.MF
└── test
└── java
└── org.itstack.demo.test
└── ApiTest.java
pom.xml (引入javassist并打入到Agent包中)
<properties>
<!-- Build args -->
<argline>-Xms512m -Xmx512m</argline>
<skip_maven_deploy>false</skip_maven_deploy>
<updateReleaseInfo>true</updateReleaseInfo>
<project.build.sourceEncoding>utf-8</project.build.sourceEncoding>
<maven.test.skip>true</maven.test.skip>
<!-- 自定义MANIFEST.MF -->
<maven.configuration.manifestFile>src/main/resources/META-INF/MANIFEST.MF</maven.configuration.manifestFile>
</properties>
<dependencies>
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.12.1.GA</version>
<type>jar</type>
</dependency>
</dependencies>
<!-- 将javassist包打包到Agent中 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
<configuration>
<artifactSet>
<includes>
<include>javassist:javassist:jar:</include>
</includes>
</artifactSet>
</configuration>
</plugin>
MyAgent.java
/**
* 博客:http://itstack.org
* 论坛:http://bugstack.cn
* 公众号:bugstack虫洞栈 {获取学习源码}
* create by fuzhengwei on 2019
*/
public class MyAgent {
//JVM 首先尝试在代理类上调用以下方法
public static void premain(String agentArgs, Instrumentation inst) {
System.out.println("this is my agent:" + agentArgs);
MyMonitorTransformer monitor = new MyMonitorTransformer();
inst.addTransformer(monitor);
}
//如果代理类没有实现上面的方法,那么 JVM 将尝试调用该方法
public static void premain(String agentArgs) {
}
}
MyMonitorTransformer.java
/**
* 博客:http://itstack.org
* 论坛:http://bugstack.cn
* 公众号:bugstack虫洞栈 {获取学习源码}
* create by fuzhengwei on 2019
*/
public class MyMonitorTransformer implements ClassFileTransformer {
private static final Set<String> classNameSet = new HashSet<>();
static {
classNameSet.add("org.itstack.demo.test.ApiTest");
}
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
try {
String currentClassName = className.replaceAll("/", ".");
if (!classNameSet.contains(currentClassName)) { // 提升classNameSet中含有的类
return null;
}
System.out.println("transform: [" + currentClassName + "]");
CtClass ctClass = ClassPool.getDefault().get(currentClassName);
CtBehavior[] methods = ctClass.getDeclaredBehaviors();
for (CtBehavior method : methods) {
enhanceMethod(method);
}
return ctClass.toBytecode();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private void enhanceMethod(CtBehavior method) throws Exception {
if (method.isEmpty()) {
return;
}
String methodName = method.getName();
if ("main".equalsIgnoreCase(methodName)) {
return;
}
final StringBuilder source = new StringBuilder();
// 前置增强: 打入时间戳
// 保留原有的代码处理逻辑
source.append("{")
.append("long start = System.nanoTime();\n") //前置增强: 打入时间戳
.append("$_ = $proceed($$);\n") //调用原有代码,类似于method();($$)表示所有的参数
.append("System.out.print(\"method:[")
.append(methodName).append("]\");").append("\n")
.append("System.out.println(\" cost:[\" +(System.nanoTime() - start)+ \"ns]\");") // 后置增强,计算输出方法执行耗时
.append("}");
ExprEditor editor = new ExprEditor() {
@Override
public void edit(MethodCall methodCall) throws CannotCompileException {
methodCall.replace(source.toString());
}
};
method.instrument(editor);
}
}
MANIFEST.MF
Manifest-Version: 1.0
Premain-Class: org.itstack.demo.agent.MyAgent
Can-Redefine-Classes: true
ApiTest.java
/**
* 博客:http://itstack.org
* 论坛:http://bugstack.cn
* 公众号:bugstack虫洞栈 {获取学习源码}
* create by fuzhengwei on 2019
*
* VM options:
* -javaagent:E:\itstack\GIT\itstack.org\itstack-demo-agent\itstack-demo-agent-02\target\itstack-demo-agent-02-1.0.0-SNAPSHOT.jar=testargs
*
*/
public class ApiTest {
public static void main(String[] args) {
ApiTest apiTest = new ApiTest();
apiTest.echoHi();
}
private void echoHi(){
System.out.println("hi agent");
}
}
this is my agent:testargs
transform: [org.itstack.demo.test.ApiTest]
hi agent
method:[echoHi] cost:[294845ns]
上一篇:基于JavaAgent的全链路监控一《嗨!JavaAgent》
下一篇:基于JavaAgent的全链路监控三《ByteBuddy操作监控方法字节码》
微信搜索「bugstack虫洞栈」公众号,关注后回复「基于JavaAgent的全链路监控」获取本文源码&更多原创专题案例!
小傅哥(微信:fustack),公众号:bugstack虫洞栈
| bugstack.cn - 沉淀、分享、成长,让自己和他人都能有所收获!
🌏 知识星球:码农会锁
实战项目:「DDD+RPC分布式抽奖系统
」、专属小册、问题解答、简历指导、架构图稿、视频课程
🐲 头条
-
💥
🎁 Lottery 抽奖系统
- 基于领域驱动设计的四层架构的互联网分布式开发实践 -
小傅哥的《重学 Java 设计模式》
- 全书彩印、重绘类图、添加内容 -
⭐小傅哥的《Java 面经手册》
- 全书5章29节,417页11.5万字,完稿&发版 -
小傅哥的《手撸 Spring》
- 通过带着读者手写简化版 Spring 框架,了解 Spring 核心原理 -
🌈小傅哥的《SpringBoot 中间件设计和开发》
- 小册16个中间件开发30个代码库
⛳ 目录
💋 精选
🐾 友链
建立本开源项目的初衷是基于个人学习与工作中对 Java 相关技术栈的总结记录,在这里也希望能帮助一些在学习 Java 过程中遇到问题的小伙伴,如果您需要转载本仓库的一些文章到自己的博客,请按照以下格式注明出处,谢谢合作。
作者:小傅哥
链接:https://bugstack.cn
来源:bugstack虫洞栈
2021年10月24日,小傅哥
的文章全部开源到代码库 CodeGuide
中,与同好同行,一起进步,共同维护。
这里我提供 3 种方式:
-
提出
Issue
:在 Issue 中指出你觉得需要改进/完善的地方(能够独立解决的话,可以在提出 Issue 后再提交PR
)。 -
处理
Issue
: 帮忙处理一些待处理的Issue
。 -
提交
PR
: 对于错别字/笔误这类问题可以直接提交PR
,无需提交Issue
确认。
详细参考:CodeGuide 贡献指南 - 非常感谢你的支持,这里会留下你的足迹
- 加群交流 本群的宗旨是给大家提供一个良好的技术学习交流平台,所以杜绝一切广告!由于微信群人满 100 之后无法加入,请扫描下方二维码先添加作者 “小傅哥” 微信(fustack),备注:加群。
- 公众号(bugstack虫洞栈) - 沉淀、分享、成长,专注于原创专题案例,以最易学习编程的方式分享知识,让自己和他人都能有所收获。
感谢以下人员对本仓库做出的贡献或者对小傅哥的赞赏,当然不仅仅只有这些贡献者,这里就不一一列举了。如果你希望被添加到这个名单中,并且提交过 Issue 或者 PR,请与我联系。