Pinpoint 插件开发 - APM

关于Pinpoint的插件开发,请首先参考Pinpoint插件开发开发者文档GitHub地址:https://github.com/naver/pinpoint/wiki/Pinpoint-Plugin-Developer-Guide ,了解其原理方便开发。

开发者文档中已有的内容这里就不再进行翻译了,本文主要讲解对插件开发的认识以及简要介绍插件的开发或改造过程。

理解

  1. Pinpoint插件的作用:通过agent模块的引用,记录访问请求或者方法的调用轨迹。 这么说可能比较抽象,大家看一下web运行界面中的图,红色框所示即为插件记录轨迹栈的效果,通过这个轨迹栈可以方便的查看到每一个请求的方法调用过程,包括异常出现的具体位置、异常原因、调用了那些方法、某一个方法的执行时间等。不过这个调用栈是多个插件协同工作产生的成果,这里之所以说是协同工作,是因为插件之间存在一定的联系,他们从轨迹记录开始共用一个TransactionID形成一个共用栈,但是插件彼此之间却互不影响,一个插件出问题不会影响其他插件的工作

本地图片0

  1. Pinpoint插件开发或者改造的必要性:Pinpoint插件本身具有很大的局限性,不具有普遍适应性。比如Pinpoint1.5.2版本中的log4j插件本身只适用于1.2.15 及以上版本,低于这个版本的log4j引用将不确保能够记录到其调用轨迹(这主要要看log4j的核心组件是否变更了,通常情况下插件是记录的核心类的相关调用,如果核心类没有变化的话则低版本的一样是可以记录到其调用的)。Pinpoint对于没有的插件将不会记录到其调用轨迹,比如,项目中如果使用了log4j2来记录日志,由于没有相应的插件,因此记录不到有关log4j2的调用轨迹。特别是一些第三方客户端,比如memcached客户端,有spymemcached、danga-java-memcached等实现,Pinpoint插件实现了spymemcached的监控,即如果是通过spymemcached客户端对memcached的核心调用大部分是会记录到调用栈的,而如果是通过danga-java-memcached客户端对memcached的调用则不被记录,表现在web模块就是看不到memcached的调用记录
  2. 官方文档介绍Pinpoint插件主要包括两部分:TraceMetadataProvider 和ProfilerPlugin实现。个人觉得这两个对应的实现主要是在客户端启动(即agent被加载时)的时候执行,插件的实现除了这两个的关键实现外,还包括一些其他的实现,比如拦截器,作用也非常重要,本文将在下文进行详细介绍。

开发或改造过程

  1. 下面是mybatis插件模块的代码结构

本地图片1

在被监控应用(引用了agent的应用)启动的时候,Pinpoint agent会跟随着启动,同时它会加载TraceMetadataProvider 和ProfilerPlugin的实现,而加载的过程是需要位于META-INF/services目录下的配置文件的支持,如图所示位于resources目录下的两个配置文件,它们的具体配置内容很简单。TraceMetadataProvider 配置文件记录了TraceMetadataProvider 的实现类,此类的主要作用是提供ServiceType 和AnnotationKey等元数据,以便标识分类其相关的轨迹数据;而ProfilerPlugin配置文件则记录了ProfilerPlugin的实现类,此类的主要作用是设置记录调用轨迹的核心类。

大多数情况下的插件开发,这两个文件及其实现类都是必须的。但也有例外,但是如果缺少其中一个则会缺少其对应的功能,比如log4j插件里就只有ProfilerPlugin的配置及其实现类,因为log4j不提供TraceMetadataProvider的实现,Pinpoint建议log单独做日志检索系统集成到web中,所以hbase中并不记录程序的log日志轨迹情况。

  1. 编写TraceMetadataProvider 和ProfilerPlugin的实现类。
  • 首先是pom文件
  • 然后就是编写实现类了,如图所示。ProfilerPlugin对应的实现类MyBatisPlugin类,实现两个接口中的setup和setTransformTemplate方法 本地图片2

MyBatisPlugin类中的setup方法是agent加载插件时调用的方法,我们通常在这个方法里配置一些我们想要记录的类、方法及相应的拦截器类来处理这些类和方法等。如图所示,mybatis插件针对methodFilter对象中的一些方法添加了拦截器SqlSessionOperationInterceptor进行处理,而没有添加此拦截器的方法,则不会进入拦截器进行处理,表现就是不记录相应的轨迹数据。 本地图片3

开发过程中遇到过多次想要的数据没有被记录的现象,主要原因就是添加的被记录类或者方法不正确,比如异常记录,刚开始以为只要添加到源方法(程序调用的方法),如果此方法有异常就能够记录异常轨迹,实则不然,必须添加到抛出此异常的最终类的方法才能够记录到异常原因

TraceMetadataProvider对应的实现类MyBatisMetadataProvider实现了TraceMetadataProvider接口,里面的setup方法用来添加插件相应的元数据,可用于轨迹数据的分类,表现就是图中的API部分

本地图片4

  1. 以上两步,两个实现类就完成了。接下来我们来看下拦截器里面主要实现。如下图所示。这里拦截器的主要目的是记录轨迹数据,比如服务元数据、异常、调用api等,如果像是redis等可以分布式部署的软件,其插件也可以记录对应的ip地址等,其实这里可以记录的数据有很多,我们只要记录需要的数据就好了,官方文档也说了,不是所有的数据都需要开发者去关心嘛。

本地图片5

关于拦截器,Pinpoint提供了众多的拦截器接口,代码在bootstrap-core模块,你可以根据自己的需要去实现不同的拦截器,如图所示。另外需要注意的是,插件调用的拦截器可以有多个

本地图片6

  1. 至此,插件代码开发完毕。使用mvn打包,然后将jar包放入agent模块指定位置,重启部署agent的应用即可。关于插件开发的测试,可以参考官方文档,也可以自己将项目全部引入IDEA,自行调试测试