基于JavaAgent的全链路监控二《通过字节码增加监控执行耗时》

简介: 通过上一章节的介绍《嗨!JavaAgent》,我们已经知道通过配置-javaagent:文件.jar后,在java程序启动时候会执行premain方法。接下来我们使用javassist字节码增强的方式,来监控方法程序的执行耗时。

7.jpg章节列表

  • 基于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编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。

环境准备

1、IntelliJ IDEA Community Edition

2、jdk1.8.0_45 64位

配置信息

(路径相关修改为自己的)

1、配置位置:Run/Debug Configurations -> VM options

2、配置内容:-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]
目录
相关文章
|
机器学习/深度学习 人工智能 自然语言处理
AI技术在自然语言处理中的应用与挑战
【10月更文挑战第3天】本文将探讨AI技术在自然语言处理(NLP)领域的应用及其面临的挑战。我们将分析NLP的基本原理,介绍AI技术如何推动NLP的发展,并讨论当前的挑战和未来的趋势。通过本文,读者将了解AI技术在NLP中的重要性,以及如何利用这些技术解决实际问题。
|
编译器
overleaf 参考文献引用,创建引用目录.bib文件,在文档中引用参考文献,生成参考文献列表
overleaf 参考文献引用,创建引用目录.bib文件,在文档中引用参考文献,生成参考文献列表
9284 0
心理健康管理系统 【毕业设计系统】
这篇文章介绍了一个心理健康管理系统的毕业设计项目,展示了系统的功能分布、登录注册界面以及不同用户角色的后台管理界面,并提供了技术栈和效果截图。
心理健康管理系统 【毕业设计系统】
|
安全 Java
Java“不兼容类型” 错误怎么查找解决
在 Java 中遇到“不兼容类型”错误时,首先理解错误信息,它表明试图将一种类型赋给不兼容的类型。检查代码中类型不匹配的赋值、方法调用参数类型不匹配、表达式类型不兼容及泛型类型不匹配等问题。解决方法包括进行类型转换、修改代码逻辑、检查方法参数和返回类型以及处理泛型类型不匹配。通过这些步骤,可以有效解决“不兼容类型”错误,确保代码类型兼容性良好。
2526 9
|
机器学习/深度学习 人工智能 算法
基于深度学习的【蔬菜识别】系统实现~Python+人工智能+TensorFlow+算法模型
蔬菜识别系统,本系统使用Python作为主要编程语言,通过收集了8种常见的蔬菜图像数据集('土豆', '大白菜', '大葱', '莲藕', '菠菜', '西红柿', '韭菜', '黄瓜'),然后基于TensorFlow搭建卷积神经网络算法模型,通过多轮迭代训练最后得到一个识别精度较高的模型文件。在使用Django开发web网页端操作界面,实现用户上传一张蔬菜图片识别其名称。
651 0
基于深度学习的【蔬菜识别】系统实现~Python+人工智能+TensorFlow+算法模型
|
Java API Android开发
安卓应用程序开发的新手指南:从零开始构建你的第一个应用
【10月更文挑战第20天】在这个数字技术不断进步的时代,掌握移动应用开发技能无疑打开了一扇通往创新世界的大门。对于初学者来说,了解并学习如何从无到有构建一个安卓应用是至关重要的第一步。本文将为你提供一份详尽的入门指南,帮助你理解安卓开发的基础知识,并通过实际示例引导你完成第一个简单的应用项目。无论你是编程新手还是希望扩展你的技能集,这份指南都将是你宝贵的资源。
615 5
|
关系型数据库 数据库 PostgreSQL
在docker上部署postgresSQL主从
通过以上步骤,我们完成了在Docker环境中部署PostgreSQL主从复制的基本配置。请注意,实际生产环境中还需考虑安全性增强(如SSL加密)、监控、自动故障切换等高级配置。此外,根据具体的业务需求和规模,可能还需要考虑使用更专业的解决方案或工具,如Patroni、PgBouncer等,来进一步提升数据库集群的稳定性和效率。
906 0
|
监控 测试技术 持续交付
Python 3.x与Python 2.x:不兼容性的深度解析
Python 3.x与Python 2.x之间的不兼容性是一个复杂而重要的问题。尽管迁移可能会带来一些挑战和困难,但考虑到Python 2.x已经停止支持以及Python 3.x带来的诸多改进和优势,迁移是不可避免的。通过了解变化、使用兼容工具、逐步迁移、利用社区资源、编写测试、保持更新、考虑使用Python 3.x的特定功能、重新评估第三方库和框架、备份和版本控制以及测试和部署等策略,你可以成功地将你的代码从Python 2.x迁移到Python 3.x,并享受Python 3.x带来的新功能和改进.
1547 5
|
存储 安全 Java
【Java】已解决Java中的java.lang.VerifyError异常
【Java】已解决Java中的java.lang.VerifyError异常
1178 1
|
jenkins 持续交付
使用 Jenkinsfile 实现流水线即代码 (Pipeline as Code)
【8月更文第31天】在现代软件开发实践中,持续集成(CI)和持续部署(CD)已经成为不可或缺的一部分。Jenkins 是一个非常流行的 CI/CD 工具,它支持多种方式来定义构建流程,其中“流水线即代码”(Pipeline as Code)是一种将构建逻辑版本化并纳入源代码管理的方法。这种方式不仅使得构建流程更加透明,也方便团队协作和版本控制。
1098 0

热门文章

最新文章