前言
据了解公司使用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#defineprotected 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做类增强。后续为分析是如何增强的,怎样构建链路信息?