前言
据了解公司使用SkyWalking做链路日志指标记录,并且由devlops团队配置,对业务开发无感。但处于好奇心,那必须拉下源码debug调试一番。
提出问题
- 怎样搭建源码调试环境?
- skywalking-java是如何利用java-agent做类增强?
问题分析
搭建源码调试环境
- 从github拉取skyWalking服务端的源码,切到最新分支v9.1.0,分别启动OAPServerBootstrap(链路指标上报服务)和ApplicationStartUp(UI)
1.1 如果出现编译错误则设置 gRPC 的自动生成的代码目录,为源码目录 - 1.2 访问localhost:8080,进入以下页面表示服务端已经启动成功
- 从github拉取skywalking-java的源码(agent,指标采集),切到最新分支v8.11.0。将其与业务代码保持同级(参考芋道源码文章)
- 2.1 执行maven package打skywalking-java的jar包
- 2.2 在业务项目中配置vm options
skywalking-java类增强源码分析
java agent
- 查看apm-sniffer/apm-agent的pom文件,在ManiFest属性中指定了“Premain-Class”
- 在指定的Premain-Class中实现agent启动方法premain,在目标JVM启动的同时加载Agent
- 在业务系统启动参数增加-javaagent:[path](其中path为对应的agent的jar包路径),就会执行SkyWalkingAgent#premain方法
bytebuddy类增强
- 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);
}
- 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)));
}
- 访问业务方法,发现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做类增强。后续为分析是如何增强的,怎样构建链路信息?