引言
追踪的基本方法是拦截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 { |
引导类插件
扫描二维码,分享此文章