字节码编程之bytebuddy结合javaagent支持多种监控方式
打印方法执行耗时是监控,获取程序运行的JVM信息是监控,链路追踪也是监控。本文看下如何实现一个通用的监控解决方案。
·
写在前面
打印方法执行耗时是监控,获取程序运行的JVM信息是监控,链路追踪也是监控。
本文看下如何实现一个通用的监控解决方案。
1:程序
定义premain:
package com.dahuyou.multi.monitor;
import com.dahuyou.multi.monitor.factory.PluginFactory;
import com.dahuyou.multi.monitor.linktrace.LinkTraceAdvice;
import com.dahuyou.multi.monitor.plugin.IPlugin;
import com.dahuyou.multi.monitor.plugin.InterceptPoint;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.utility.JavaModule;
import java.lang.instrument.Instrumentation;
import java.util.List;
public class MyPreMain {
public static void premain(String agentArgs, Instrumentation inst) {
System.out.println("Java agent配置参数:" + agentArgs);
System.out.println("============================");
System.out.println("大忽悠🐂B监控系统开始工作了!");
System.out.println("============================");
AgentBuilder agentBuilder = new AgentBuilder.Default();
// 获取所有可用的监控插件(jvm,链路追踪,执行耗时等)
List<IPlugin> pluginGroup = PluginFactory.pluginGroup;
for (IPlugin plugin : pluginGroup) {
InterceptPoint[] interceptPoints = plugin.buildInterceptPoint();
for (InterceptPoint point : interceptPoints) {
AgentBuilder.Transformer transformer
= (builder, typeDescription, classLoader, javaModule) -> {
builder = builder.visit(Advice.to(plugin.adviceClass()).on(point.buildMethodsMatcher()));
return builder;
};
agentBuilder = agentBuilder.type(point.buildTypesMatcher()).transform(transformer).asDecorator();
}
}
// 将bytebuddy的插桩逻辑安装到instrument
agentBuilder.with(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) {
// System.out.println("onTransformation:" + typeDescription);
}
@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) {
}
}).installOn(inst);
}
}
这里将不同的监控类型抽象为插件,定义了监控插件接口类:
package com.dahuyou.multi.monitor.plugin;
/**
* 监控插件接口
*/
public interface IPlugin {
/*
插件名称
*/
String pluginName();
/*
插件的监控点(即监控哪些类的哪些方法)
*/
InterceptPoint[] buildInterceptPoint();
/*
执行具体监控的切面类
*/
Class adviceClass();
}
本文实现,打印JVM,和链路追踪两种监控方式,实现类如下:
package com.dahuyou.multi.monitor.plugin.concrete;
import com.dahuyou.multi.monitor.jvmusage.JVMUsageAdvice;
import com.dahuyou.multi.monitor.plugin.IPlugin;
import com.dahuyou.multi.monitor.plugin.InterceptPoint;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;
/**
* jvm使用情况插件
*/
public class JvmPlugin implements IPlugin {
@Override
public String pluginName() {
return "jvmusage";
}
@Override
public InterceptPoint[] buildInterceptPoint() {
return new InterceptPoint[]{
new InterceptPoint() {
@Override
public ElementMatcher<TypeDescription> buildTypesMatcher() {
return ElementMatchers.nameStartsWith("com.dahuyou.multi.monitor");
}
@Override
public ElementMatcher<MethodDescription> buildMethodsMatcher() {
return ElementMatchers.named("method1")
.or(ElementMatchers.named("method2"))
.or(ElementMatchers.named("method3"));
}
}
};
}
@Override
public Class adviceClass() {
return JVMUsageAdvice.class;
}
}
package com.dahuyou.multi.monitor.plugin.concrete;
import com.dahuyou.multi.monitor.jvmusage.JVMUsageAdvice;
import com.dahuyou.multi.monitor.linktrace.LinkTraceAdvice;
import com.dahuyou.multi.monitor.plugin.IPlugin;
import com.dahuyou.multi.monitor.plugin.InterceptPoint;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;
/**
* 链路追踪插件
*/
public class LinkTracePlugin implements IPlugin {
@Override
public String pluginName() {
return "linktrace";
}
@Override
public InterceptPoint[] buildInterceptPoint() {
return new InterceptPoint[]{
new InterceptPoint() {
@Override
public ElementMatcher<TypeDescription> buildTypesMatcher() {
return ElementMatchers.nameStartsWith("com.dahuyou.multi.monitor");
}
@Override
public ElementMatcher<MethodDescription> buildMethodsMatcher() {
return ElementMatchers.named("method1")
.or(ElementMatchers.named("method2"))
.or(ElementMatchers.named("method3"));
}
}
};
}
@Override
public Class adviceClass() {
return LinkTraceAdvice.class;
}
}
通过插件工厂PluginFactory来维护所有可用的监控插件:
package com.dahuyou.multi.monitor.factory;
import com.dahuyou.multi.monitor.plugin.IPlugin;
import com.dahuyou.multi.monitor.plugin.concrete.JvmPlugin;
import com.dahuyou.multi.monitor.plugin.concrete.LinkTracePlugin;
import java.util.ArrayList;
import java.util.List;
/**
* 可用监控插件工厂类
*/
public class PluginFactory {
public static List<IPlugin> pluginGroup = new ArrayList<>();
static {
//链路追踪监控
pluginGroup.add(new LinkTracePlugin());
//Jvm监控插件
pluginGroup.add(new JvmPlugin());
}
}
接口InterceptPoint,用来抽象监控点,接口定义如下:
package com.dahuyou.multi.monitor.plugin;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
/**
* 插件监控点接口,设置插件要监控的类和要监控的方法
*/
public interface InterceptPoint {
/*
设置插件要监控的类
*/
ElementMatcher<TypeDescription> buildTypesMatcher();
/*
设置插件要监控的方法
*/
ElementMatcher<MethodDescription> buildMethodsMatcher();
}
核心逻辑就是这些了,详细的大家感兴趣的话还是看源码。
接着来打包:
测试类:
package com.dahuyou.multi.monitor.test;
public class ApiTest {
public static void main(String[] args) {
//线程一
new Thread(() -> new ApiTest().method1(), "线程思密达").start();
// new Thread(() -> new ApiTest().method1(), "线程萨瓦迪卡").start();
// new ApiTest().method1();
}
public void method1() {
System.out.println("测试结果:hi1");
method2();
}
public void method2() {
System.out.println("测试结果:hi2");
method3();
}
public void method3() {
System.out.println("测试结果:hi3");
}
}
配置javaagent:
运行测试:
写在后面
参考文章列表
更多推荐
所有评论(0)