官方文档:
链接: skywalking-agent

1. SkyWalking 插件架构概述

SkyWalking 插件的工作机制主要基于 Java Agent,它会在应用启动时自动附加到目标应用上。插件主要实现方法拦截(Method Intercept),即在指定的方法执行之前、之后或者发生异常时进行操作,以便收集监控数据。
每个插件通常包括以下几个模块:

  • 增强类定义(Enhancement Class):指定需要增强的目标类。
  • 方法拦截器(Method Interceptor):定义拦截的逻辑,比如调用链的开始、结束、异常捕获等。
  • 上下文传递(Context Propagation):在分布式追踪中,将上下文信息(如 TraceId)传递到其他线程或请求中。

2. 插件工程

在这里插入图片描述

  • 在 my-custom-plugin 中的 src/main/resources/skywalking-plugin.def 文件中定义插件的入口类。
aa=com.example.plugin.MyCustomPluginInstrumentation

3. 开发一个插件

  • 需求:为特定的类方法创建span,使该方法可以在trace链路中显示出来,监控该方法

3.1 定义插件入口类

  • 插件入口类(Instrumentation)用于指定目标类和目标方法。通过实现 ClassInstanceMethodsEnhancePluginDefine 接口来定义哪个类和方法需要增强。
public class DynamicInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {

    private static final Map<String, List<String>> CLASS_METHOD_MAP = InterceptConfigManager.getClassMethodMap();


    @Override
    protected ClassMatch enhanceClass() {
        // 返回所有需要增强的类
        String join = String.join(",", CLASS_METHOD_MAP.keySet());
        return MultiClassNameMatch.byMultiClassMatch(String.join(",", CLASS_METHOD_MAP.keySet()));
    }

    @Override
    public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
        return new ConstructorInterceptPoint[0];
    }

    @Override
    public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
        List<InstanceMethodsInterceptPoint> points = new ArrayList<>();
        // 指定需要拦截的方法和拦截器
        CLASS_METHOD_MAP.forEach((className, methods) -> {
            methods.forEach(method ->{
                points.add(new InstanceMethodsInterceptPoint() {
                    @Override
                    public ElementMatcher<MethodDescription> getMethodsMatcher() {
                        return named(method);
                    }

                    @Override
                    public String getMethodsInterceptor() {
                        return "org.apache.skywalking.apm.method.statistics.ExampleMethodInterceptor";
                    }

                    @Override
                    public boolean isOverrideArgs() {
                        return false;
                    }
                });
            });

        });

        InstanceMethodsInterceptPoint[] array = new InstanceMethodsInterceptPoint[points.size()];
        array = points.toArray(array);
        return array;
    }
}

3.2 创建拦截器

  • 拦截器用于定义具体的拦截逻辑。通过实现 InstanceMethodsAroundInterceptor 接口,可以定义在方法执行的前、后、异常等阶段执行的逻辑。
  • 在ExampleMethodInterceptor 中:
    • beforeMethod:方法开始前执行,这里创建一个新的 Span。
    • afterMethod:方法执行后执行,结束当前 Span。
    • handleMethodException:在方法执行中出现异常时执行,将异常记录到 Span 中,以便追踪和监控异常情况。
public class ExampleMethodInterceptor implements InstanceMethodsAroundInterceptor {

    @Override
    public void beforeMethod(
            EnhancedInstance objInst,
            Method method,
            Object[] allArguments,
            Class<?>[] argumentsTypes,
            MethodInterceptResult result) throws Throwable {
        // 创建 LocalSpan
        String className = method.getDeclaringClass().getName();
        String methodName = method.getName();

        AbstractSpan span = ContextManager.createLocalSpan(className + "/"+methodName);
    }

    @Override
    public Object afterMethod(
            EnhancedInstance objInst,
            Method method,
            Object[] allArguments,
            Class<?>[] argumentsTypes,
            Object ret) throws Throwable {
        // 结束 LocalSpan
        ContextManager.stopSpan();
        return ret;
    }

    @Override
    public void handleMethodException(
            EnhancedInstance objInst,
            Method method,
            Object[] allArguments,
            Class<?>[] argumentsTypes,
            Throwable t) {
        // 捕获异常并记录到 Span
        ContextManager.activeSpan().errorOccurred().log(t);
    }

}

3.3 读取特定方法,指定config文件

public class InterceptConfigManager {

    private static final String CONFIG_FILE = "config/apm-test.config";
    private static final Map<String, List<String>> CLASS_METHOD_MAP = new ConcurrentHashMap<>();

    static {
        loadConfig();
    }

    public static void loadConfig() {
        try {
            File configFile = new File(AgentPackagePath.getPath(), CONFIG_FILE);
            List<String> lines = Files.readAllLines(configFile.toPath());
            CLASS_METHOD_MAP.clear();
            for (String line : lines) {
                String[] parts = line.split("=");
                if (parts.length == 2) {
                    String className = parts[0].trim();
                    String[] methods = parts[1].split(",");
                    CLASS_METHOD_MAP.put(className, Arrays.asList(methods));
                }
            }
            System.out.println("Config loaded: " + CLASS_METHOD_MAP);
        } catch (IOException e) {
            throw new RuntimeException("Failed to load plugin configuration: " + CONFIG_FILE, e);
        } catch (AgentPackageNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    public static Map<String, List<String>> getClassMethodMap() {
        return CLASS_METHOD_MAP;
    }

}

3.4 配置 skywalking-plugin.def

在 skywalking-plugin.def 文件中,注册插件入口类,使得 SkyWalking 可以加载该插件:

myself=org.apache.skywalking.apm.method.statistics.DynamicInstrumentation

3.4 配置 config文件

  • /skywalking-agent/config 创建apm-test.config
com.xx.package.A=method1,method2
com.xx.package.B=method3,method4

3.5 重启服务重新加载agent即可,观察sw-ui,这里不在演示啦

Logo

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

更多推荐