skywalking05 - skywalking探针插件开发
探针与toolkit注解的区别
在上一章,我们通过了@Trace, @Tags, @Tag注解,对我们的方法进行了链路追踪. 但是它有这样三点不好:
- 代码侵入, 直接写在了我们业务系统的代码里,但是它本身和业务一点都不相关.
- 不可复用, 注解都加在具体的方法上,我们无法移植到其他工程中,完成链路追踪.
- 限制性大, 只能注解在我们自己写的代码中,不能对其他三方包中的方法进行追踪.
而探针则在这三个问题上,得到了解决.我们接下来的demo,将会以Base64.encodeToString()进行展示.
探针的基本概念
- Span : 可以理解为一次方法调用\数据库查询\一次RPC访问
- Trace Segment : 属于Skywalking特有的概念. 在支持多线程的语言中,一个线程中所有的操作归属于一个Span的聚合,这些Span具有同一个SegmentId.
- ContextCarrier : 跨进程调用链的链接.
- ContextSnapshot: 跨线程的调用链的链接.
核心对象
插件开发基本绕不过去的类,具体解释暂时懒得打了,建议直接看书的第十章,闲了后补
- org.apache.skywalking.apm.agent.core.context.ContextCarrier
- org.apache.skywalking.apm.agent.core.context.ContextManager
- org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan
- org.apache.skywalking.apm.agent.core.context.tag.Tags
- org.apache.skywalking.apm.agent.core.context.AsyncSpan
工程结构
插件开发三要素
- *Instrumentation 定义拦截的方式:
- 匹配类名 enhanceClass()
- 匹配构造方法 getConstructorsInterceptPoints()
- 匹配方法 getInstanceMethodsInterceptPoints()
/** * #3,告诉skywalking拦截哪些方法,并指定拦截器 * ClassEnhancePluginDefine 父类 * ClassInstanceMethodsEnhancePluginDefine 实例方法 * ClassStaticMethodsEnhancePluginDefine 静态方法 */ public class Base64Instrumentation extends ClassInstanceMethodsEnhancePluginDefine { private static final String ENHANCE_CLASS = "org.apache.commons.codec.binary.Base64"; private static final String INTERCEPT_CLASS = "org.dfg.demo.sk.plugin.foo.Base64EncodeInterceptor"; @Override protected ClassMatch enhanceClass() { return NameMatch.byName(ENHANCE_CLASS); } /** * 拦截构造器 * * @return */ @Override public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { return null; } /** * 拦截方法 * InstanceMethodsAroundInterceptor 实例方法 * InstanceConstructorInterceptor 构造方法 * StaticMethodsAroundInterceptor 静态方法 * * @return */ @Override public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { //拦截实例方法 return new InstanceMethodsInterceptPoint[]{ new InstanceMethodsInterceptPoint() { @Override public ElementMatcher<MethodDescription> getMethodsMatcher() { //拦截方法,支持多种匹配规则 return named("encodeToString") // .and(takesArguments(1)) // .and(takesArguments(byte[].class)) ; } @Override public String getMethodsInterceptor() { return INTERCEPT_CLASS; } @Override public boolean isOverrideArgs() { return false; } } }; } }
- *Interceptor 定义拦截器实际方法
- 方法执行前 beforeMethod()
- 方法执行后 afterMethod()
- 方法发生异常 handleMethodException()
/** * #4,skywalking拦截到指定方法后回调 * 在这里面获取调用情况如方法、参数等,并记录span */ public class Base64EncodeInterceptor implements InstanceMethodsAroundInterceptor { public static final OfficialComponent BASE64 = new OfficialComponent(301, "BASE64"); @Override public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, MethodInterceptResult result) throws Throwable { //创建span AbstractSpan span = ContextManager.createLocalSpan("base64.encode"); //设置组件类型 span.setComponent(BASE64); //获取参数 byte[] param = (byte[]) allArguments[0]; //记录span tag new StringTag("source").set(span, Arrays.toString(param)); //记录span SpanLayer.asHttp(span); } @Override public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Object ret) throws Throwable { if (ret != null) { AbstractSpan span = ContextManager.activeSpan(); //span.errorOccurred(); new StringTag("result").set(span, String.valueOf(ret)); } //结束span ContextManager.stopSpan(); return ret; } @Override public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Throwable t) { AbstractSpan abstractSpan = ContextManager.activeSpan(); abstractSpan.log(t); } }
工程仍位于同一个demo中,skywalking-plugin-example
- *.def 插件说明文件,告诉探针,插件类型\插件定义的类的全类名
foo-plugin=org.dfg.demo.sk.plugin.foo.define.Base64Instrumentation
插件打包
可以替换bytebuddy包路径,在pom.xml的节点下,非必须,只是demo中将打出来的jar包会传输到agent包中的plugin目录中
<plugin> <artifactId>maven-shade-plugin</artifactId> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> 中 </goals> <configuration> <shadedArtifactAttached>false</shadedArtifactAttached> <createDependencyReducedPom>false</createDependencyReducedPom> <createSourcesJar>false</createSourcesJar> <shadeSourcesContent>true</shadeSourcesContent> <relocations> <relocation> <pattern>${shade.net.bytebuddy.source}</pattern> <shadedPattern>${shade.net.bytebuddy.target}</shadedPattern> </relocation> </relocations> </configuration> </execution> </executions> </plugin>
插件复制
如果替换了bytebuddy包路径,则无需手动复制.替换结果:
wanglh@dark:~/IdeaProjects/skywalking-plugin-example/apache-skywalking-apm-bin-es7/agent/plugins$ ls -al 总用量 2616 drwxr-xr-x 2 wanglh wanglh 12288 2月 5 20:58 . drwxr-xr-x 9 wanglh wanglh 4096 2月 4 21:51 .. -rw-r--r-- 1 wanglh wanglh 21274 2月 4 17:11 apm-activemq-5.x-plugin-8.3.0.jar -rw-r--r-- 1 wanglh wanglh 13197 2月 4 17:11 apm-dubbo-plugin-8.3.0.jar -rw-r--r-- 1 wanglh wanglh 28217 2月 4 17:11 apm-ehcache-2.x-plugin-8.3.0.jar -rw-r--r-- 1 wanglh wanglh 6999 2月 5 20:58 skywalking-plugin-foo-1.0-SNAPSHOT.jar wanglh@dark:~/IdeaProjects/skywalking-plugin-example/apache-skywalking-apm-bin-es7/agent/plugins$ ls -al *foo* -rw-r--r-- 1 wanglh wanglh 6999 2月 5 20:58 skywalking-plugin-foo-1.0-SNAPSHOT.jar wanglh@dark:~/IdeaProjects/skywalking-plugin-example/apache-skywalking-apm-bin-es7/agent/plugins$
可以观察到foo插件包的时间戳是最新的
运行查看结果
运行工程
与上一章相同的启动方式
访问URL
方式不变,仍为:
curl http://localhost:8081/foo?p=a
查看链路
已经可以看到新增的encode方法的链路
查看Tag
// 入口 //记录span tag new StringTag("source").set(span, Arrays.toString(param)); //记录span SpanLayer.asHttp(span); // 出口 if (ret != null) { AbstractSpan span = ContextManager.activeSpan(); //span.errorOccurred(); new StringTag("result").set(span, String.valueOf(ret)); } //结束span ContextManager.stopSpan();
以上是demo工程中,对该Span添加的Tag.
实际图也可以观察到生效了: