javaagent笔记及一个基于javassit的应用监控程序demo
javaagent基本用法
定义入口premain
public static void premain(String agentArgs, Instrumentation inst) {
System.out.println("Hello, world! JavaAgent");
System.out.println("agentArgs: " + agentArgs); inst.addTransformer(new APMAentV1());
}
设置
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.6</version>
<configuration>
<archive>
<manifestEntries>
<Premain-Class>com.liq.app2.APMAgentV1</Premain-Class>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
定义转换器对class进行处理
public class APMAentV1 implements ClassFileTransformer {
@Override
public byte[] transform(ClassLoader classLoader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
...
}
}
给目标程序的启动增加-javaagent参数
-javaagent:/home/xxx/code/app2-1.0-SNAPSHOT.jar=javaagentArg1,javaagentArg2
# 后面的javaagentArg1,javaagentArg2会传入premain的agentArgs
这样,目标程序就会加载这个javaagent了
Javassit
javassit是一个开源的分析、编辑和创建Java字节码的工具。
优点:简单、快速,不需要了解虚拟机指令。
类似的工具还有ASM。
比较而言,javassit性能没有ASM高,所以一般可以使用javaassit实现功能之后,再基于ASM去优化性能。
javaassit使用步骤:
构造ClassPool对象;
插入类查找路径:insertClassPath();
获取CTclass;
- 构建新类makeClass;
- get加载已有类(找不到会报NotFound异常);
修改:
- 修改方法,如addMethod;
- 修改字段,如addField;
- 等等;
生成类并装载class:
- toClass;
- toByteCode;
例子
import javassist.*; public class JavaSsistDemo1 {
public static void main(String[] args) throws NotFoundException, CannotCompileException, IllegalAccessException, InstantiationException {
ClassPool pool = new ClassPool(true);
pool.insertClassPath(new LoaderClassPath(JavaSsistDemo1.class.getClassLoader())); CtClass targetClass = pool.makeClass("com.liq.Hello");
targetClass.addInterface(pool.get(IHello.class.getName()));
String methodName = "sayHello";
CtClass returnType = pool.get(void.class.getName());
CtClass[] parameters = new CtClass[]{pool.get(String.class.getName())};
CtMethod method = new CtMethod(returnType, methodName, parameters, targetClass); String src = "{" +
"System.out.println(\"Hello \" + $1);" +
"}";
method.setBody(src);
targetClass.addMethod(method); Class cls = targetClass.toClass();
IHello hello = (IHello) cls.newInstance();
hello.sayHello("Tom");
} public interface IHello {
void sayHello(String name);
}
}
基于Javassit的一个简单应用程序监控的javaagent的完整简单例子
APMAgentV1.java
package com.liq.app2; import com.liq.javaagent.collector.*;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.LoaderClassPath; import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; public class APMAgentV1 implements ClassFileTransformer { private static Collector[] collectors; static {
collectors = new Collector[]{OtherCollector.INSTANCE};
} private Map<ClassLoader, ClassPool> classPoolMap = new ConcurrentHashMap<>(); @Override
public byte[] transform(ClassLoader classLoader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
if ((className == null) || (classLoader == null) || (classLoader.getClass().getName().equals("sun.reflect.DelegatingClassLoader")) || (classLoader.getClass().getName().equals("org.apache.catalina.loader.StandardClassLoader")) || (classLoader.getClass().getName().equals("javax.management.remote.rmi.NoCallStackClassLoader")) || (classLoader.getClass().getName().equals("com.alibaba.fastjson.util.ASMClassLoader")) || (className.indexOf("$Proxy") != -1) || (className.startsWith("java"))) {
return null;
} // 不同的ClassLoader使用不同的ClassPool
ClassPool localClassPool;
if (!this.classPoolMap.containsKey(classLoader)) {
localClassPool = new ClassPool();
localClassPool.insertClassPath(new LoaderClassPath(classLoader));
this.classPoolMap.put(classLoader, localClassPool);
} else {
localClassPool = this.classPoolMap.get(classLoader);
} try {
className = className.replaceAll("/", ".");
CtClass localCtClass = localClassPool.get(className);
for (Collector collector : collectors) {
if (collector.isTarget(className, classLoader, localCtClass)) {
byte[] arrayOfByte = collector.transform(classLoader, className, classfileBuffer, localCtClass);
System.out.println(String.format("%s APM agent insert success", new Object[]{className}));
return arrayOfByte;
}
}
} catch (Throwable localThrowable) {
new Exception(String.format("%s APM agent insert fail", new Object[]{className}), localThrowable).printStackTrace();
}
return new byte[0];
} public static void premain(String agentArgs, Instrumentation inst) {
System.out.println("Hello, world! JavaAgen");
System.out.println("agentArgs: " + agentArgs); inst.addTransformer(new APMAgentV1());
}
}ClassWrapper.java
用于向目标类插入一些监控代码package com.liq.app2; import javassist.CtMethod;
import javassist.NotFoundException; public class ClassWrapper {
private String beginSrc;
private String endSrc;
private String errorSrc; public ClassWrapper beginSrc(String paramString) {
this.beginSrc = paramString;
return this;
} public ClassWrapper endSrc(String paramString) {
this.endSrc = paramString;
return this;
} public ClassWrapper errorSrc(String paramString) {
this.errorSrc = paramString;
return this;
} public String beginSrc(CtMethod ctMethod) {
try {
String template = ctMethod.getReturnType().getName().equals("void")
?
"{\n" +
" %s \n" +
" try {\n" +
" %s$agent($$);\n" +
" } catch (Throwable e) {\n" +
" %s\n" +
" throw e;\n" +
" }finally{\n" +
" %s\n" +
" }\n" +
"}"
:
"{\n" +
" %s \n" +
" Object result=null;\n" +
" try {\n" +
" result=($w)%s$agent($$);\n" +
" } catch (Throwable e) {\n" +
" %s \n" +
" throw e;\n" +
" }finally{\n" +
" %s \n" +
" }\n" +
" return ($r) result;\n" +
"}"; String insertBeginSrc = this.beginSrc == null ? "" : this.beginSrc;
String insertErrorSrc = this.errorSrc == null ? "" : this.errorSrc;
String insertEndSrc = this.endSrc == null ? "" : this.endSrc;
String result = String.format(template, new Object[]{insertBeginSrc, ctMethod.getName(), insertErrorSrc, insertEndSrc});
return result;
} catch (NotFoundException localNotFoundException) {
throw new RuntimeException(localNotFoundException);
}
}
}
ClassReplacer.java
用于使用包装过的类替换目标类package com.liq.app2; import javassist.CannotCompileException;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod; import java.io.IOException; public class ClassReplacer
{
private final String className;
private final ClassLoader classLoader;
private final CtClass ctClass; public ClassReplacer(String className, ClassLoader classLoader, CtClass ctClass)
{
this.className = className;
this.classLoader = classLoader;
this.ctClass = ctClass;
} public void replace(CtMethod ctMethod, ClassWrapper paramd) throws CannotCompileException {
String methodName = ctMethod.getName();
CtMethod localCtMethod2 = CtNewMethod.copy(ctMethod, methodName, this.ctClass, null);
localCtMethod2.setName(methodName + "$agent");
this.ctClass.addMethod(localCtMethod2);
ctMethod.setBody(paramd.beginSrc(ctMethod));
} public byte[] replace() throws IOException, CannotCompileException {
return this.ctClass.toBytecode();
}
}
Collector.java
监控信息搜集器接口package com.liq.app2.collector; import javassist.CtClass; public interface Collector { boolean isTarget(String className, ClassLoader classLoader, CtClass ctClass); byte[] transform(ClassLoader classLoader, String className, byte[] classfileBuffer, CtClass ctClass); }OtherCollector.java
监控信息搜集器实现,这里只是简单例子,所以写死了目标类名字“com.liq.service.UserService”package com.liq.app2.collector; import com.liq.app2.ClassReplacer;
import com.liq.app2.ClassWrapper;
import com.liq.app2.stat.Statistics;
import javassist.*; public class OtherCollector implements Collector { public static final OtherCollector INSTANCE = new OtherCollector(); private OtherCollector() {
} private static final String beginSrc;
private static final String endSrc = "inst.end(statistic);";
private static final String errorSrc; static {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("com.liq.app2.collector.OtherCollector inst=com.liq.app2.collector.OtherCollector.INSTANCE;");
stringBuilder.append("com.liq.app2.stat.Statistics statistic = inst.start(\"%s\");");
beginSrc = stringBuilder.toString();
errorSrc = "inst.error(statistic,e);";
} @Override
public boolean isTarget(String className, ClassLoader classLoader, CtClass ctClass) {
return "com.liq.service.UserService".equals(className);
} @Override
public byte[] transform(ClassLoader classLoader, String className, byte[] classfileBuffer, CtClass ctClass) {
try {
ClassReplacer replacer = new ClassReplacer(className, classLoader, ctClass);
for (CtMethod ctMethod : ctClass.getDeclaredMethods()) {
String str;
if ((Modifier.isPublic(ctMethod.getModifiers())) && (!Modifier.isStatic(ctMethod.getModifiers()) && (!Modifier.isNative(ctMethod.getModifiers())))) {
ClassWrapper classWrapper = new ClassWrapper();
classWrapper.beginSrc(String.format(beginSrc, ctMethod.getLongName()));
classWrapper.endSrc(endSrc);
classWrapper.errorSrc(errorSrc); replacer.replace(ctMethod, classWrapper);
}
}
return replacer.replace();
} catch (Exception e) {
e.printStackTrace();
} return new byte[0];
} public Statistics start(String methodSign) {
return new Statistics(methodSign);
} public void end(Statistics statistics) {
statistics.end();
} public void error(Statistics statistics, Throwable e) {
statistics.error(e);
}
}Statistics
package com.liq.app2.stat; public class Statistics { private String method;
private long startTime;
private long endTime;
private Throwable error; public Statistics(String method) {
this.method = method;
this.startTime = System.currentTimeMillis();
} public void end() {
endTime = System.currentTimeMillis(); System.out.println(this.toString());
} public void error(Throwable e) {
error = e;
} @Override
public String toString() {
final StringBuilder sb = new StringBuilder("Statistics{");
sb.append("method='").append(method).append('\'');
sb.append(", startTime=").append(startTime);
sb.append(", endTime=").append(endTime);
sb.append(", error=").append(error);
sb.append('}');
return sb.toString();
}
}maven依赖
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.12.1.GA</version>
</dependency>
javaagent笔记及一个基于javassit的应用监控程序demo的更多相关文章
- 一个基于ES5的vue小demo
由于现在很多vue项目都是基于ES6开发的,而我学vue的时候大多是看vue官网的API,是基于ES5的,所以对于刚接触项目的我来说要转变为项目的模块化写法确实有些挑战.因此,我打算先做一个基于ES5 ...
- 一个基于netty的websocket聊天demo
这里,仅仅是一个demo,模拟客户基于浏览器咨询卖家问题的场景,但是,这里的demo中,卖家不是人,是基于netty的程序(我就叫你uglyRobot吧),自动回复了客户问的问题. 项目特点如下: 1 ...
- C# 开源一个基于 yarp 的 API 网关 Demo,支持绑定 Kubernetes Service
关于 Neting 刚开始的时候是打算使用微软官方的 Yarp 库,实现一个 API 网关,后面发现坑比较多,弄起来比较麻烦,就放弃了.目前写完了查看 Kubernetes Service 信息.创建 ...
- 一个基于ES6+webpack的vue小demo
上一篇文章<一个基于ES5的vue小demo>我们讲了如何用ES5,vue-router做一个小demo,接下来我们来把它变成基于ES6+webpack的demo. 一.环境搭建及代码转换 ...
- 一个基于vue的时钟
前两天写了一个基于vue的小钟表,给大家分享一下. 其中时针和分针使用的是图片,结合transform制作:表盘刻度是通过transform和transformOrigin配合画的:外面的弧形框框,啊 ...
- 一个基于mysql构建的队列表
通常大家都会使用redis作为应用的任务队列表,redis的List结构,在一段进行任务的插入,在另一端进行任务的提取. 任务的插入 $redis->lPush("key:task:l ...
- ASP.NET MVC Web API 学习笔记---第一个Web API程序
http://www.cnblogs.com/qingyuan/archive/2012/10/12/2720824.html GetListAll /api/Contact GetListBySex ...
- (一)学习了解OrchardCore笔记——开篇:基于asp.net core的OrchardCore
想深入了解OrchadCore源码许久了,但是读源码的时候遇到很多问题而网上的参考资料太少了(几乎都是OrchadCms不带OrchardCore的),现在解决得差不多了,做下笔记方便自己查看,有错误 ...
- psutil一个基于python的跨平台系统信息跟踪模块
受益于这个模块的帮助,在这里我推荐一手. https://pythonhosted.org/psutil/#processes psutil是一个基于python的跨平台系统信息监视模块.在pytho ...
随机推荐
- winform 中TextBox只能输入数字
textBox1.KeyPress+=TextNumber_KeyPress; private void TextNumber_KeyPress(object sender, KeyPressEven ...
- 洛谷P2147[SDOI2008]洞穴勘测(lct)
题目描述 辉辉热衷于洞穴勘测. 某天,他按照地图来到了一片被标记为JSZX的洞穴群地区.经过初步勘测,辉辉发现这片区域由n个洞穴(分别编号为1到n)以及若干通道组成,并且每条通道连接了恰好两个洞穴.假 ...
- vs项目属性中的包含目录和库目录以及附加依赖项全都配置正确了,却还是提示:无法解析的外部符号
这种情况下,很大可能是lib文件有问题 我是用vs编译下载的源代码文件得到的lib出现了如题的情况, 后来去网站上直接下载了lib文件,竟然解决了!-.-
- Android-ContentProvider简单的增删改查
注意:在ContentProvider里面写对数据库增删改查的时候,千万不能 db.close(); cursor.close(); 等操作,不然其他应用访问不到数据,也没有必要写isOpen(); ...
- Iframe 高度自适应 example (跨子域实现)
跨子域的iframe高度自适应 比如 'https://www.kzwr.com/topics/baidu' 嵌入了 'http://pan.kzwr.com/',这种跨子域的页面,实现起来也比较简单 ...
- Verilog MIPS32 CPU(五)-- CP0
Verilog MIPS32 CPU(一)-- PC寄存器 Verilog MIPS32 CPU(二)-- Regfiles Verilog MIPS32 CPU(三)-- ALU Verilog M ...
- [Erlang07] Erlang 做图形化编程的尝试:纯Erlang做2048游戏
用Erlang久了,以为erlang做类似于As3,JS的图形化界面是绝对不可能的,多少次,多少次想用erlang做个炫酷的图形游戏.终于:折腾出来了结果:纯Erlang也可以做到! 因为以前接触过W ...
- 'System.ValueTuple, Version=0.0.0.0 required for Add-Migration on .NET 4.6.1 Class Library
https://stackoverflow.com/questions/45978173/system-valuetuple-version-0-0-0-0-required-for-add-migr ...
- Selenium爬取元素下子元素方法
Selenium提供了一下方法来定义一个页面中的元素: find_element_by_id find_element_by_name find_element_by_xpath find_eleme ...
- css3动画(animation)效果3-正方体合成
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...