字节码javaagent的全链路监控篇二,通过字节码增加监控执行耗时(代码片段)

九师兄 九师兄     2023-01-15     388

关键词:

1.概述

上一篇文章:【字节码】javaagent 入门 案例 最简单的案例

转载:https://github.com/fuzhengwei/itstack-demo-bytecode

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

Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的。它已加入了开放源代码JBoss应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态"AOP"框架。

关于java字节码的处理,目前有很多工具,如bcel,asm。不过这些都需要直接跟虚拟机指令打交道。如果你不想了解虚拟机指令,可以采用javassist。javassist是jboss的一个子项目,其主要的优点,在于简单,而且快速。直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。

2.代码示例

代码结构如下


maven设置如下


    <dependencies>
        <dependency>
            <groupId>javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.12.1.GA</version>
        </dependency>
        <dependency>
            <groupId>javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.12.1.GA</version>
            <type>jar</type>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>utf-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifestEntries>
                            <addClasspath>true</addClasspath>
                            <Premain-Class>com.javaagent.MyAgent</Premain-Class>
                            <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
                            <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>


我们的agent类如下

package com.javaagent;

import java.lang.instrument.Instrumentation;

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

package com.javaagent;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtBehavior;
import javassist.CtClass;
import javassist.expr.ExprEditor;
import javassist.expr.MethodCall;

import java.lang.instrument.ClassFileTransformer;
import java.security.ProtectionDomain;
import java.util.HashSet;
import java.util.Set;

public class MyMonitorTransformer implements ClassFileTransformer 

    private static final Set<String> classNameSet = new HashSet<>();

    static 
        classNameSet.add("com.javaagent.MyAgentTest");
    

    @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);
    



我们的测试类如下

package com.javaagent;

import org.junit.Test;

import static org.junit.Assert.*;

public class MyAgentTest 


    public static void main(String[] args) throws Exception 
        MyAgentTest apiTest = new MyAgentTest();
        apiTest.echoHi();

        /****************************************************
         * 测试结果
         * this is my agent:testargs
         * transform: [org.itstack.demo.test.ApiTest]
         * hi agent
         * method:[echoHi] cost:[195纳秒]
         ****************************************************/
    

    private void echoHi() throws Exception 
        System.out.println("hi agent");
    

然后我们执行打包命令

lcc@lcc javaagent-demo2$ mvn clean  package

执行完毕后,添加如下参数

-javaagent:/Users/lcc/IdeaProjects/lcc_work/test-javaagent/javaagent-demo2/target/javaagent-demo2-1.0-SNAPSHOT.jar=testargs

然后运行com.javaagent.MyAgentTest结果如下

this is my agent:testargs
transform: [com.javaagent.MyAgentTest]
hi agent
method:[echoHi] cost:[83220ns]

字节码基于javaagent的全链路监控五-threadlocal链路追踪(代码片段)

1.概述转载:基于JavaAgent的全链路监控五《ThreadLocal链路追踪》Google开源的Dapper链路追踪组件,并在2010年发表了论文《Dapper,aLarge-ScaleDistributedSystemsTracingInfrastructure》,这篇文章是业内实现链路追踪的标杆和理论基础&#x... 查看详情

字节码基于javaagent的全链路监控六基于jvmti定位java异常信息(代码片段)

1.概述上一篇文章:【字节码】基于JavaAgent的全链路监控六开发应用级监控本章节转载:基于JavaAgent的全链路监控六《开发应用级监控》JVMTI(JVMToolInterface)位于jpda最底层,是Java虚拟机所提供的native编程接口。JVMTI可以... 查看详情

字节码基于javaagent的全链路监控四-jvm内存与gc信息(代码片段)

1.概述转载:基于JavaAgent的全链路监控四《JVM内存与GC信息》2.案例简述除了监控java方法的执行耗时,我们还需要获取应用实例的jvm内存与gc信息,以实时把控我们的服务器性能是否在安全范围。监控jvm内存与gc信息是... 查看详情

字节码javaagent入门案例最简单的案例(代码片段)

...种机制(Instrumentation)可以在加载class文件之前修改方法的字节码(此时字节码尚未加入JVM),动态更改类方法实现AOP,提供监控服务如;方法调用时长、可用率、内存等。本章节初步怎么让java代码执行时可以进入我们的agent方法。开... 查看详情

基线监控:基于依赖关系的全链路智能监控报警

更多技术交流、求职机会、试用福利,欢迎关注字节跳动数据平台微信公众号,回复【1】进入官方交流群字节跳动数据平台开发套件数据开发团队自研了基于依赖关系的全链路智能监控报警——基线监控,目前已在字节跳动内... 查看详情

字节码增强技术之javaagent入门(代码片段)

文章目录前言JavaAgent简介JavaInstrumentation核心方法JavaAgent核心流程JavaAgent使⽤demo预演总结前言分布式链路追踪中为了获取服务之间调用链信息,采集器通常需要在方法的前后做埋点。在Java生态中,常见的埋点方式有两种&#... 查看详情

字节码插桩之javaagent(代码片段)

字节码插桩之JavaAgent本篇文章将详细讲解有关JavaAgent的知识,揭开它神秘的面纱,帮助开发人员了解它的黑魔法,帮助我们完成更多业务需求WhatisJavaAgentJavaAgent又称为Java探针,它提供了向现有已编译的Java类添加... 查看详情

字节码javassist通过字节码插桩监控方法采集运行时入参出参和异常信息(代码片段)

1.概述上一篇文章:【字节码】使用Javassist在运行时重新加载类「替换原方法输出不一样的结果」实时加载类转载来源于:小傅哥的字节码编程-(公众号:bugstack虫洞栈)仅供学习。转载:https://github.com/fuzhengwei/itstac... 查看详情

字节码javaagentbytebuddy操作监控方法字节码(代码片段)

1.概述上一篇文章:【字节码】javaagent入门案例最简单的案例转载:https://github.com/fuzhengwei/itstack-demo-bytecode在第二章中我们已经可以监控方法执行耗时,虽然它能完成我们一些基本需要,但是为了增强代码的扩展... 查看详情

字节码增强技术之javaagent入门(代码片段)

...外一种方式:依赖JavaAgent技术,修改目标方法的字节码,做到无侵入的埋点。这种利用JavaAgent的方式的采集器,也叫做探针。在应用程序启动时使用-javaagent参数,或者运行时使用attach(pid)方式,就可以将探... 查看详情

javaagent简介(代码片段)

...叫做Java探针,是在JDK1.5引入的一种可以动态修改Java字节码的技术。Java类编译之后形成字节码被JVM执行,在JVM在执行这些字节码之前获取这些字节码信息,并且通过字节码转换器对这些字节码进行修改,来完成一... 查看详情

javaagent简介(代码片段)

...叫做Java探针,是在JDK1.5引入的一种可以动态修改Java字节码的技术。Java类编译之后形成字节码被JVM执行࿰ 查看详情

javaagent简介(代码片段)

...叫做Java探针,是在JDK1.5引入的一种可以动态修改Java字节码的技术。Java类编译之后形成字节码被JVM执行࿰ 查看详情

java综合专栏「渐入佳境」全链路追踪原理之javaagent探针的底层运作原理和分析(中篇)(代码片段)

...JDK1.5之后引入的新特性,此特性为用户提供了在JVM将字节码文件读入内存之后,JVM使用对应的字节流在Java堆中生成一个Class对象之前,用户可以对其字节码进行修改的能力,从而JVM也将会使用用户修改过之后的字... 查看详情

字节码byte-buddy监控方法执行耗时动态获取出入参类型和值(代码片段)

1.概述上一篇文章:【字节码】基于ByteBuddy语法创建的第一个HelloWorld转载:https://github.com/fuzhengwei/itstack-demo-bytecode案例是剥去外衣包装展示出核心功能的最佳学习方式!就像是我们研究字节码编程最终是需要应用到实... 查看详情

字节码插桩之javaagent(代码片段)

字节码插桩之JavaAgent本篇文章将详细讲解有关JavaAgent的知识,揭开它神秘的面纱,帮助开发人员了解它的黑魔法,帮助我们完成更多业务需求WhatisJavaAgentJavaAgent又称为Java探针,它提供了向现有已编译的Java类添加... 查看详情

字节码插桩之javaagent(代码片段)

字节码插桩之JavaAgent本篇文章将详细讲解有关JavaAgent的知识,揭开它神秘的面纱,帮助开发人员了解它的黑魔法,帮助我们完成更多业务需求WhatisJavaAgentJavaAgent又称为Java探针,它提供了向现有已编译的Java类添加... 查看详情

javaagent和java字节码增强技术的学习与实践

参考文章:    https://www.cnblogs.com/chiangchou/p/javassist.html    https://blog.csdn.net/u010039929/article/details/62881743    https://www.jia 查看详情