写在前面
打印方法执行耗时是监控,获取程序运行的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的插桩逻辑安装到instrumentagentBuilder.with(new AgentBuilder.Listener() {@Overridepublic void onDiscovery(String s, ClassLoader classLoader, JavaModule javaModule, boolean b) {}@Overridepublic void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule, boolean b, DynamicType dynamicType) {
// System.out.println("onTransformation:" + typeDescription);}@Overridepublic void onIgnored(TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule, boolean b) {}@Overridepublic void onError(String s, ClassLoader classLoader, JavaModule javaModule, boolean b, Throwable throwable) {}@Overridepublic 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 {@Overridepublic String pluginName() {return "jvmusage";}@Overridepublic InterceptPoint[] buildInterceptPoint() {return new InterceptPoint[]{new InterceptPoint() {@Overridepublic ElementMatcher<TypeDescription> buildTypesMatcher() {return ElementMatchers.nameStartsWith("com.dahuyou.multi.monitor");}@Overridepublic ElementMatcher<MethodDescription> buildMethodsMatcher() {return ElementMatchers.named("method1").or(ElementMatchers.named("method2")).or(ElementMatchers.named("method3"));}}};}@Overridepublic 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 {@Overridepublic String pluginName() {return "linktrace";}@Overridepublic InterceptPoint[] buildInterceptPoint() {return new InterceptPoint[]{new InterceptPoint() {@Overridepublic ElementMatcher<TypeDescription> buildTypesMatcher() {return ElementMatchers.nameStartsWith("com.dahuyou.multi.monitor");}@Overridepublic ElementMatcher<MethodDescription> buildMethodsMatcher() {return ElementMatchers.named("method1").or(ElementMatchers.named("method2")).or(ElementMatchers.named("method3"));}}};}@Overridepublic 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:
运行测试: