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. 设计模式7---Java动态代理机制详解(JDK 和CGLIB,Javassist,ASM)

    class文件简介及加载 Java编译器编译好Java文件之后,产生.class 文件在磁盘中.这种class文件是二进制文件,内容是只有JVM虚拟机能够识别的机器码.JVM虚拟机读取字节码文件,取出 ...

  2. gulp-usemin 插件使用

    关于什么是gulp,它和grunt有什么区别等问题,这里不做任何介绍.本文主要介绍如何使用gulp-usemin这款插件,同时也会简单介绍本文中用到的一些插件. 什么是gulp-usemin 用来将H ...

  3. Gulp资料大全:入门、插件、脚手架、包清单

    awesome-gulp中文版 一份gulp的资源,插件和使用实例清单, 致力于打造更好的前端工程构建流程. 被老外的awesome 清单刺激到,觉得有必要翻译一份,为国产的程序员们做点事情,本清单将 ...

  4. python文件操作os模块

    Python 统计某一文件夹下文件数量 使用python  pathlib模块 from pathlib import Path dir_path = ' ' print(len(list(Path( ...

  5. Delphi 调试连接 任意Android手机/平板/盒子(要安装Google USB Driver,并且还有USB的相关许多文章)

    Delphi有时候无法连接调试一些手机,解决方案: 1.安装Google USB Driver 2.通过设备管理器查看手机或平板USB的VID,PID 3.修改你的电脑上的android_winusb ...

  6. ansible 常用模块的使用

    安装 yum -y install ansible 配置文件/etc/ansible/hosts 模块介绍与使用 ping模块 [root@node1 config]# ansible k8s -m ...

  7. vim 插入时间戳的方法

    这里主要说明用内置函数 strftime 来插入,而不用 :r!date 或类似方法. 用命令 "=strftime('%c')<Ret>p ,或<C-r>=strf ...

  8. Spring定时任务执行

    备注:这个是基于搭建好spring的环境下的 注解方式: 1.定时任务类 package com.test;import java.util.Date;import org.springframewo ...

  9. [Maven实战-许晓斌]-[第二章]-2.7-2.8 Mave安装的最优建议和安装小结

    2.7

  10. [ActionScript 3.0] 动态绘制扇形实例(拖拽绘制)

    package { import flash.display.Shape; import flash.display.Sprite; import flash.events.MouseEvent; / ...