基于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]
目录
相关文章
|
6月前
|
Shell 开发工具
【优化篇】telegraf+shell脚本实现秒级的服务状态异常监测与告警
【优化篇】telegraf+shell脚本实现秒级的服务状态异常监测与告警
152 0
|
Arthas Cloud Native Java
是谁在调用我?使用 arthas+jprofiler 做复杂链路分析
Arthas 是阿里巴巴开源的应用诊断利器,提供了 profiler 命令,可以生成热点火焰图。通过采样录制调用链路来做性能分析,极大提升了线上排查性能问题的效率。
是谁在调用我?使用 arthas+jprofiler 做复杂链路分析
|
3月前
|
监控 Java API
分布式链路监控系统问题之对Java应用实现字节码增强的方式的问题如何解决
分布式链路监控系统问题之对Java应用实现字节码增强的方式的问题如何解决
|
3月前
|
监控 Java API
分布式链路监控系统问题之Attach实现字节码增强的问题如何解决
分布式链路监控系统问题之Attach实现字节码增强的问题如何解决
|
4月前
|
缓存 监控 算法
如何优化Java应用的响应时间
如何优化Java应用的响应时间
|
4月前
|
JSON 监控 安全
优化Java应用程序的日志记录和跟踪
优化Java应用程序的日志记录和跟踪
|
5月前
|
Arthas 监控 Java
Java项目方法调用链路耗时追踪(接口优化)
Java项目方法调用链路耗时追踪(接口优化)
226 0
|
Java Linux
JAVA CPU过载问题排查思路以及一键查询脚本
JAVA CPU过载问题排查思路以及一键查询脚本
150 0
|
XML 存储 监控
java日志(系统运行监控)
.什么是日志? 记录系统运行的过程和详情,并可以进行永久存储
244 0
java日志(系统运行监控)
|
存储 缓存 监控
深度探索JFR - JFR详细介绍与生产问题定位落地 - 1. JFR说明与启动配置(上)
深度探索JFR - JFR详细介绍与生产问题定位落地 - 1. JFR说明与启动配置(上)
深度探索JFR - JFR详细介绍与生产问题定位落地 - 1. JFR说明与启动配置(上)