javaagent基本用法

  1. 定义入口premain

    public static void premain(String agentArgs, Instrumentation inst) {
    System.out.println("Hello, world! JavaAgent");
    System.out.println("agentArgs: " + agentArgs); inst.addTransformer(new APMAentV1());
    }
  2. 设置

    <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>
  3. 定义转换器对class进行处理

    public class APMAentV1 implements ClassFileTransformer {
    @Override
    public byte[] transform(ClassLoader classLoader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
    ...
    }
    }
  4. 给目标程序的启动增加-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使用步骤:

  1. 构造ClassPool对象;

  2. 插入类查找路径:insertClassPath();

  3. 获取CTclass;

    1. 构建新类makeClass;
    2. get加载已有类(找不到会报NotFound异常);
  4. 修改:

    1. 修改方法,如addMethod;
    2. 修改字段,如addField;
    3. 等等;
  5. 生成类并装载class:

    1. toClass;
    2. toByteCode;
  6. 例子

    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的完整简单例子

  1. 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());
    }
    }
  2. 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);
    }
    }
    }
  3. 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();
    }
    }
  4. 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);
    
    }
    
    
  5. 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);
    }
    }
  6. 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();
    }
    }
  7. maven依赖

    <dependency>
    <groupId>javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.12.1.GA</version>
    </dependency>

javaagent笔记及一个基于javassit的应用监控程序demo的更多相关文章

  1. 一个基于ES5的vue小demo

    由于现在很多vue项目都是基于ES6开发的,而我学vue的时候大多是看vue官网的API,是基于ES5的,所以对于刚接触项目的我来说要转变为项目的模块化写法确实有些挑战.因此,我打算先做一个基于ES5 ...

  2. 一个基于netty的websocket聊天demo

    这里,仅仅是一个demo,模拟客户基于浏览器咨询卖家问题的场景,但是,这里的demo中,卖家不是人,是基于netty的程序(我就叫你uglyRobot吧),自动回复了客户问的问题. 项目特点如下: 1 ...

  3. C# 开源一个基于 yarp 的 API 网关 Demo,支持绑定 Kubernetes Service

    关于 Neting 刚开始的时候是打算使用微软官方的 Yarp 库,实现一个 API 网关,后面发现坑比较多,弄起来比较麻烦,就放弃了.目前写完了查看 Kubernetes Service 信息.创建 ...

  4. 一个基于ES6+webpack的vue小demo

    上一篇文章<一个基于ES5的vue小demo>我们讲了如何用ES5,vue-router做一个小demo,接下来我们来把它变成基于ES6+webpack的demo. 一.环境搭建及代码转换 ...

  5. 一个基于vue的时钟

    前两天写了一个基于vue的小钟表,给大家分享一下. 其中时针和分针使用的是图片,结合transform制作:表盘刻度是通过transform和transformOrigin配合画的:外面的弧形框框,啊 ...

  6. 一个基于mysql构建的队列表

    通常大家都会使用redis作为应用的任务队列表,redis的List结构,在一段进行任务的插入,在另一端进行任务的提取. 任务的插入 $redis->lPush("key:task:l ...

  7. ASP.NET MVC Web API 学习笔记---第一个Web API程序

    http://www.cnblogs.com/qingyuan/archive/2012/10/12/2720824.html GetListAll /api/Contact GetListBySex ...

  8. (一)学习了解OrchardCore笔记——开篇:基于asp.net core的OrchardCore

    想深入了解OrchadCore源码许久了,但是读源码的时候遇到很多问题而网上的参考资料太少了(几乎都是OrchadCms不带OrchardCore的),现在解决得差不多了,做下笔记方便自己查看,有错误 ...

  9. psutil一个基于python的跨平台系统信息跟踪模块

    受益于这个模块的帮助,在这里我推荐一手. https://pythonhosted.org/psutil/#processes psutil是一个基于python的跨平台系统信息监视模块.在pytho ...

随机推荐

  1. C#中遇到的方法总结

    1.Select(string filterExpression, string sort)  // 获取按照指定的排序顺序且与筛选条件相匹配的所有 System.Data.DataRow 对象的数组 ...

  2. Hadoop的Windows伪分布式学习

    解压hadoop-2.7.2.zip,不是tar.gz,前者是Windows所用的 解压到路径,设置环境变量 HADOOP_HOME=E:\hadoop-2.7.2\ HADOOP_USER_HOME ...

  3. Python-面向对象编程01_什么是面向对象

    Python从设计之初就已经是一门面向对象的语言了,正因如此,在Python中创建一个类和对象是很容易的. 什么是面向对象? 面向对象程序设计(Object-oriented programming, ...

  4. Jquery ajax的参数格式化

    jQuery的ajax会自动将js对象转换为可传递的参数,$.param(jsobj, boolean),但是默认会把对象中数组类型加上[]符号,后台就不怎么好取了 参数boolean为true时不加 ...

  5. 利用Trace.WriteLine定位难以重现的问题

    最近的一个项目中,在客户测试环境(UAT)发现了一个bug,却反复尝试都无法在开发环境和QA环境来重现.界面上也没有出现任何异常和错误,只是某个数据的显示错误,其他数据都正常.仔细分析和调试了出错位置 ...

  6. Java集合框架(Collection Framework)学习之 HashMap

    从API文档可以得到HashMap的以下几个特点: 基于哈希表(hash table)实现,并且是链式哈希表 允许空值和空键(null=null 键值对) HashMap与Hashtable基本相同, ...

  7. [HAOI2010]计数(组合数学)(数位DP)

    原题题意也就是给的数的全排列小于原数的个数. 我们可以很容易的想到重复元素的排列个数的公式. 但是我们发现阶乘的话很快就会爆long long啊(如果您想写高精请便) 之后我就尝试质因数分解....但 ...

  8. OpenCV库文件介绍

    以前都是直接用opencv,都没有仔细研究过,这次把库文件都介绍一下. 1.build和source 当我们安装完opencv中,你会发现在opencv文件夹中有两个文件夹,build和source, ...

  9. Django-04模板层

    你可能已经注意到我们在例子视图中返回文本的方式有点特别. 也就是说,HTML被直接硬编码在 Python代码之中. def current_datetime(request): now = datet ...

  10. vm虚拟机Kali无法拖拽文件解决办法

    vm虚拟机Kali无法拖拽文件解决办法 apt-get updateapt-get install open-vm-tools-desktop fusereboot