引言
追踪的基本方法是拦截Java方法,使用字节码操作技术和AOP概念。SkyWalking包装了字节码操作技术,和追踪上下文的传播,所以你只需要定义拦截点(类似于 spring的切面)
拦截
SkyWalking 提供了两类通用的定义来拦截构造方法,实例方法和类方法。
- ClassInstanceMethodsEnhancePluginDefine定义了构造方法- constructor拦截点 和实例方法- instance method拦截点
- ClassStaticMethodsEnhancePluginDefine定义了类方法- class method拦截点
当然,你也可以继承 ClassEnhancePluginDefine 去设置所有拦截点,但这不常用。
插件实现
通过扩展 ClassInstanceMethodsEnhancePluginDefine 来演示如何实现一个插件。
- 定义目标类的名称ClassMatch 表示如何去匹配目标类,有4种方式:1 protected abstract ClassMatch enhanceClass(); 
- byName: 基于类的全限定名(包名 + . + 类名)。
- byClassAnnotationMatch: 根据目标类中是否存在某些注解。
- byMethodAnnotationMatch: 根据目标类的方法中是存在某些注解。
- byHierarchMatch: 基于目标类的父类或接口。
注意:
- 在插件定义中禁止使用ThirdPartyClass.class,例如takesArguments(ThirdPartyClass.class) 或byName(ThirdPartyClass.class.getName()),因为ThirdPartyClass不一定存在于目标应用程序中,这样做导致探针异常; 我们有“导入”检查来帮助在 CI 流程检查此限制,但它没有涵盖此限制的所有场景,所以永远不要视图通过使用完全限定类名 (FQCN) 之类的方法绕过这个此限制,takeArguments( full.qualified.ThirdPartyClass.class)和byName(full.qualified.ThirdPartyClass.class.getName())即使能通过CI检查,但是代理代码中仍然无效,使用完全限定的类名字符串文献替代。
- 禁止使用 *.class.getName()去获取类名,建议使用文本字符串,这事为了避免classloader的问题
- by*AnnotationMatch不支持从父类继承来的注解
- 除非确实有必要,否则不建议使用 byHierarchyMatch,因为使用它可能会触发拦截许多预期之外的方法,会导致性能问题和不稳定。
实例:
| 1 | 
 | 
- 定义实例方法拦截点
| 1 | public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints(); | 
也可以使用Matcher来设置目标方法。 如果要在拦截器中更改引用参数,请在isOverrideArgs中返回true。
- 在文件 skywalking-plugin.def 中添加插件定义1 tomcat-7.x/8.x=TomcatInstrumentation 实现一个拦截器作为一个实例方法的拦截器,它必须实现org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor在方法调用前、调用后以及异常处理阶段使用核心API。1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32/** 
 * A interceptor, which intercept method's invocation. The target methods will be defined in {@link
 * ClassEnhancePluginDefine}'s subclass, most likely in {@link ClassInstanceMethodsEnhancePluginDefine}
 */
 public interface InstanceMethodsAroundInterceptor {
 /**
 * called before target method invocation.
 *
 * @param result change this result, if you want to truncate the method.
 * @throws Throwable
 */
 void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
 MethodInterceptResult result) throws Throwable;
 /**
 * called after target method invocation. Even method's invocation triggers an exception.
 *
 * @param ret the method's original return value.
 * @return the method's actual return value.
 * @throws Throwable
 */
 Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
 Object ret) throws Throwable;
 /**
 * called when occur exception.
 *
 * @param t the exception occur.
 */
 void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
 Throwable t);
 }
引导类插件
SkyWalking 已经将引导工具打包在代理核心中。 通过在插件定义中声明它,可以很容易继承实现它。
重写 public boolean isBootstrapInstrumentation()  并返回true
| 1 | public class URLInstrumentation extends ClassEnhancePluginDefine { | 
注:
引导类插件只拦截最主要的类定义,在实际运行中会要影响JRE核心(rt.jar),随便定义它可能会产生意想不到的结果或副作用。
以Tomcat插件为例
定义目标类的名称/定义实例方法拦截点
| 1 | public class TomcatInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { | 
在文件 skywalking-plugin.def 中添加插件定义
| 1 | tomcat-7.x/8.x=org.apache.skywalking.apm.plugin.tomcat78x.define.TomcatInstrumentation | 
实现拦截器
| 1 | public class TomcatInvokeInterceptor implements InstanceMethodsAroundInterceptor { | 
引导类插件
扫描二维码,分享此文章
