前言

  据了解公司使用SkyWalking做链路日志指标记录,并且由devlops团队配置,对业务开发无感。但处于好奇心,那必须拉下源码debug调试一番。

提出问题

  1. 怎样搭建源码调试环境?
  2. skywalking-java是如何利用java-agent做类增强?

问题分析

搭建源码调试环境

  1. 从github拉取skyWalking服务端的源码,切到最新分支v9.1.0,分别启动OAPServerBootstrap(链路指标上报服务)和ApplicationStartUp(UI)
    1.1 如果出现编译错误则设置 gRPC 的自动生成的代码目录,为源码目录
  2. skywalking java agent启动参数 skywalking jvm 设置_java

  3. 1.2 访问localhost:8080,进入以下页面表示服务端已经启动成功
  4. skywalking java agent启动参数 skywalking jvm 设置_npm_02

  5. 从github拉取skywalking-java的源码(agent,指标采集),切到最新分支v8.11.0。将其与业务代码保持同级(参考芋道源码文章
  6. skywalking java agent启动参数 skywalking jvm 设置_java_03

  7. 2.1 执行maven package打skywalking-java的jar包
  8. skywalking java agent启动参数 skywalking jvm 设置_java_04

  9. 2.2 在业务项目中配置vm options
  10. skywalking java agent启动参数 skywalking jvm 设置_链路_05

skywalking-java类增强源码分析

java agent

  1. 查看apm-sniffer/apm-agent的pom文件,在ManiFest属性中指定了“Premain-Class”
  2. 在指定的Premain-Class中实现agent启动方法premain,在目标JVM启动的同时加载Agent
  3. 在业务系统启动参数增加-javaagent:[path](其中path为对应的agent的jar包路径),就会执行SkyWalkingAgent#premain方法

bytebuddy类增强

  1. skywalking通过bytebuddy类增强
public static void premain(String agentArgs, Instrumentation instrumentation) throws PluginException {
	
	final ByteBuddy byteBuddy = new ByteBuddy().with(TypeValidation.of(Config.Agent.IS_OPEN_DEBUGGING_CLASS));

    AgentBuilder agentBuilder = new AgentBuilder.Default(byteBuddy).ignore(
            nameStartsWith("net.bytebuddy.")
                    .or(nameStartsWith("org.slf4j."))
                    .or(nameStartsWith("org.groovy."))
                    .or(nameContains("javassist"))
                    .or(nameContains(".asm."))
                    .or(nameContains(".reflectasm."))
                    .or(nameStartsWith("sun.reflect"))
                    .or(allSkyWalkingAgentExcludeToolkit())
                    .or(ElementMatchers.isSynthetic()));
                    
     
     agentBuilder.type(pluginFinder.buildMatch())
     			// 类加载时会触发该转换器
                .transform(new Transformer(pluginFinder))
                .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
                .with(new RedefinitionListener())
                .with(new Listener())
                .installOn(instrumentation);
}
  1. Transformer进行类转换
    2.1 找到原始类的插件定义列表:pluginDefines
    2.2 遍历pluginDefines,对原始类进行重新定义:AbstractClassEnhancePluginDefine#define
protected DynamicType.Builder<?> enhance(TypeDescription typeDescription, DynamicType.Builder<?> newClassBuilder,
                                         ClassLoader classLoader, EnhanceContext context) throws PluginException {
	// 增强一个类以拦截类的静态方法
	newClassBuilder = this.enhanceClass(typeDescription, newClassBuilder, classLoader);
	//增强类以拦截构造函数和类实例方法
	 newClassBuilder = this.enhanceInstance(typeDescription, newClassBuilder, classLoader, context);
}

2.3 下面ClassEnhancePluginDefine#enhanceInstance是如何增强类的

protected DynamicType.Builder<?> enhanceInstance(TypeDescription typeDescription,
    DynamicType.Builder<?> newClassBuilder, ClassLoader classLoader,
    EnhanceContext context) throws PluginException {
    // 为原始类新增字段
	newClassBuilder = newClassBuilder.defineField(
                CONTEXT_ATTR_NAME, Object.class, ACC_PRIVATE | ACC_VOLATILE)
                                             .implement(EnhancedInstance.class)
                                             .intercept(FieldAccessor.ofField(CONTEXT_ATTR_NAME));
    //增强构造函数 --> ConstructorInter
    newClassBuilder = newClassBuilder.constructor(constructorInterceptPoint.getConstructorMatcher())
                                               .intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.withDefaultConfiguration()
                                                                                                             .to(new ConstructorInter(constructorInterceptPoint
                                                                                                               .getConstructorInterceptor(), classLoader))));
	//增强实例方法 --> InstMethodsInter
 	newClassBuilder = newClassBuilder.method(junction)
                                                     .intercept(MethodDelegation.withDefaultConfiguration()
                                                                                .to(new InstMethodsInter(interceptor, classLoader)));
}
  1. 访问业务方法,发现InstMethodsInter进行拦截处理
public Object intercept(@This Object obj, @AllArguments Object[] allArguments, @SuperCall Callable<?> zuper,
    @Origin Method method) throws Throwable {
    // 前置处理
	interceptor.beforeMethod(targetObject, method, allArguments, method.getParameterTypes(), result);
	// 业务方法
	ret = zuper.call();
	// 后置处理
	ret = interceptor.afterMethod(targetObject, method, allArguments, method.getParameterTypes(), ret);
}

束语

  本篇文章是SkyWalking系列学习第一篇文章,主要介绍如何搭建源码调式环境以及了解skywalking是如何基于java-agent做类增强。后续为分析是如何增强的,怎样构建链路信息?