字节码JavaAgent的全链路监控篇二,通过字节码增加监控执行 耗时
Posted 九师兄
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了字节码JavaAgent的全链路监控篇二,通过字节码增加监控执行 耗时相关的知识,希望对你有一定的参考价值。
1.概述
上一篇文章:【字节码】javaagent 入门 案例 最简单的案例
转载:https://github.com/fuzhengwei/itstack-demo-bytecode
通过上一章节的介绍,我们已经知道通过配置-javaagent:文件.jar后,在java程序启动时候会执行premain方法。接下来我们使用javassist字节码增强的方式,来监控方法程序的执行耗时
Javassist
是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的。它已加入了开放源代码JBoss应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态"AOP"框架。
关于java字节码的处理,目前有很多工具,如bcel,asm。不过这些都需要直接跟虚拟机指令打交道。如果你不想了解虚拟机指令,可以采用javassist。javassist是jboss的一个子项目,其主要的优点,在于简单,而且快速。直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。
2.代码示例
代码结构如下
maven设置如下
<dependencies>
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.12.1.GA</version>
</dependency>
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.12.1.GA</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>utf-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<addClasspath>true</addClasspath>
<Premain-Class>com.javaagent.MyAgent</Premain-Class>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
我们的agent类如下
package com.javaagent;
import java.lang.instrument.Instrumentation;
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
package com.javaagent;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtBehavior;
import javassist.CtClass;
import javassist.expr.ExprEditor;
import javassist.expr.MethodCall;
import java.lang.instrument.ClassFileTransformer;
import java.security.ProtectionDomain;
import java.util.HashSet;
import java.util.Set;
public class MyMonitorTransformer implements ClassFileTransformer
private static final Set<String> classNameSet = new HashSet<>();
static
classNameSet.add("com.javaagent.MyAgentTest");
@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);
我们的测试类如下
package com.javaagent;
import org.junit.Test;
import static org.junit.Assert.*;
public class MyAgentTest
public static void main(String[] args) throws Exception
MyAgentTest apiTest = new MyAgentTest();
apiTest.echoHi();
/****************************************************
* 测试结果
* this is my agent:testargs
* transform: [org.itstack.demo.test.ApiTest]
* hi agent
* method:[echoHi] cost:[195纳秒]
****************************************************/
private void echoHi() throws Exception
System.out.println("hi agent");
然后我们执行打包命令
lcc@lcc javaagent-demo2$ mvn clean package
执行完毕后,添加如下参数
-javaagent:/Users/lcc/IdeaProjects/lcc_work/test-javaagent/javaagent-demo2/target/javaagent-demo2-1.0-SNAPSHOT.jar=testargs
然后运行com.javaagent.MyAgentTest
结果如下
this is my agent:testargs
transform: [com.javaagent.MyAgentTest]
hi agent
method:[echoHi] cost:[83220ns]
以上是关于字节码JavaAgent的全链路监控篇二,通过字节码增加监控执行 耗时的主要内容,如果未能解决你的问题,请参考以下文章
字节码基于JavaAgent的全链路监控五- ThreadLocal链路追踪
字节码基于JavaAgent的全链路监控六 基于jvmti定位java异常信 息
字节码基于JavaAgent的全链路监控四-JVM内存与GC信息