写在前面

源码
本文看下通过bytebuddy结合javaagent如何实现监控方法耗时功能。

1:程序

首先写agent入口程序:

package com.dahuyou.monitor.method;

//import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.utility.JavaModule;

import java.lang.instrument.Instrumentation;

public class MyPreMain {

    public static void premain(String agentArgs, Instrumentation instrumentation) {
        System.out.println("the agent args is:" + agentArgs);
        AgentBuilder.Transformer transformer = (builder, typeDescription, classLoader, javaModule) -> {
            // 主要设置代理,以及拦截的方法(拦截的方法不太清楚为什么要在这里设置,不应该和设置要拦截的类在一起吗?)
            return builder.method(ElementMatchers.any())
                    .intercept(MethodDelegation.to(MyByteBuddyDelegator.class));
        };

        AgentBuilder.Listener listener = new AgentBuilder.Listener() {
            @Override
            public void onDiscovery(String s, ClassLoader classLoader, JavaModule javaModule, boolean b) {

            }

            @Override
            public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule, boolean b, DynamicType dynamicType) {

            }

            @Override
            public void onIgnored(TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule, boolean b) {

            }

            @Override
            public void onError(String s, ClassLoader classLoader, JavaModule javaModule, boolean b, Throwable throwable) {

            }

            @Override
            public void onComplete(String s, ClassLoader classLoader, JavaModule javaModule, boolean b) {

            }
        };
        new AgentBuilder
                .Default()
                .type(ElementMatchers.nameStartsWith("com.dahuyou.monitor.method")) // 指定要拦截的类包前缀
                .transform(transformer)
                .with(listener)
                .installOn(instrumentation);
    }

    /**
     * 有public static void premain(String agentArgs, Instrumentation instrumentation)这个方法,本方法不会被调用
     * @param agentArgs
     */
    public static void premain(String agentArgs) {}
}

通过builder.method(ElementMatchers.any())定义要拦截的方法,这里拦截所有方法,.type(ElementMatchers.nameStartsWith("com.dahuyou.monitor.method"))定义要拦截的包路径前缀。intercept(MethodDelegation.to(MyByteBuddyDelegator.class))指定代理类为
MyByteBuddyDelegator,符合过滤规则的方法会执行该类,源码如下:

package com.dahuyou.monitor.method;

import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;

/**
 * byte buddy 执行代理类
 */
public class MyByteBuddyDelegator {

    @RuntimeType
    public static Object intercept(@Origin Method method, @SuperCall Callable<?> callable, @AllArguments Object[] args) throws Exception {
        long start = System.currentTimeMillis();
        try {
            // 执行原方法逻辑
            return callable.call();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("方法: " + method + ", 执行耗时:" + (System.currentTimeMillis() - start) + " 毫秒");
        }
        return null;
    }
}

接着就可以来打包测试了,首先来打包:
在这里插入图片描述
测试类:

package com.dahuyou.monitor.method;

public class ApiTest {
    public static void main(String[] args) throws InterruptedException {
        ApiTest apiTest = new ApiTest();
        apiTest.echoHi();
    }


    public ApiTest() {

    }

    private void echoHi() throws InterruptedException {
        System.out.println("hi agent");
        Thread.sleep((long) (Math.random() * 500));
    }
}

配置agent:
在这里插入图片描述
运行:

the agent args is:agent
hi agent
方法: private void com.dahuyou.monitor.method.ApiTest.echoHi() throws java.lang.InterruptedException, 执行耗时:408 毫秒
方法: public static void com.dahuyou.monitor.method.ApiTest.main(java.lang.String[]) throws java.lang.InterruptedException, 执行耗时:408 毫秒

Process finished with exit code 0

写在后面

参考文章列表

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐