开发pinpoint插件log4j2

pinpoint是一个非常优秀的APM工具,适用于使用Java构建的大型分布式系统。受到论文 Dapper的启发,pinpoint提供了一套体验很好的调用链分析视图。

00 前言

首先,说明一下开发pinpoint的log4j2插件的初衷,我们最近在做微服务治理和PAAS平台,当我们做到微服务监控的时候,就遇到微服务治理中的一个重要问题,就是大量微服务被创建,并上线运行,它们之间的调用关系是怎样的?其中的数据流向是什么样?如果我们对这些信息没有一个全面的掌握的话,微服务化的系统简直就成了一堆乱麻,出了问题完全不知道从何下手。

其次,最近流行的微服务解决方案:service Mesh,又在微服务中加入了很多独立进程的组件,它们来完成微服务的治理工作,当数据流量经过它们的时候,数据是否能够完整,这些组件是否是处在正常工作的状态?都需要我们监控掌握。

这些问题不仅需要各自的监控告警,还需要一个整体调用链的监控。这时我们就选择了Pinpoint调用链监控工具,在众多的调用链监控工具(cat、skywalking、zipkin等)中,我们选择它主要从以下几个方面考虑:

  • 代码的侵入程度
  • 插件生态的完整性
  • 监控的力度
  • 是否符合标准规范(openTrancing)

综合以上的种种考虑,选择了pinpoint来实现这个调用链的监控功能。

但是由于我们选择的微服务技术栈的先进性,有个别功能pinpoint的现有插件不能完成监控,于是就需要我们做一定的定制性开发。

第一个要实现的就是log4j2日志组件的pinpoint插件,这个插件的功能就是将调用链的一些信息放在log插件的MDC中,以方便将这些信息输出打印到日志文件中。

为什么选择log4j2作为log日志的输出组件呢?官方的pinpoint插件中包含有log4j和logback的插件,如果选择这两个日志组件,pinpoint不需要改造即可完成功能需求。我们的理由是,首先log4j不能满足我们的日志文件切分和滚动覆盖的需求,logback有一些性能问题。从log日志组件的发展上看,logack是log4j的升级版,log4j2是logback的升级版,每一个升级版本都做了很多优化。所以从需求和性能出发我们选择最新的log4j2.

可能读者们还有一个疑问,为什么需要将调用链的信息打印到日志文件中呢?这是因为,当系统运行出现问题的时候,我们需要快速排查这个问题,这时候需要能将各个监控信息快速关联起来,更多有关联的信息,才有助于我们排查问题,否则一堆孤立的信息对我们帮助是不大的。通常我们的日志信息是存储在日志中心(ELK)的,我们通过一定的格式化手段将每条日志信息中都带有调用链的tranceId信息,这时我们就可以拿在pinpoint中的任意一条调用链的tranceId,到日志中心搜索出对应的所有的日志。

各种理由准备充分,确定要开发log4j2的pinpoint日志插件了。

01 准备工作

在开发之前需要做几个准备工作,以便我们能够顺利开展log4j2的插件开发。

  1. 下载源代码之前,先将代码:naver/pinpoint,fork一份到自己的仓库,方便自己提交代码。
  2. 从自己github仓库中下载fork的pinpoint源代码。
  3. 进入根目录pinpoint/下整体编译一下:mvn clean install -Dmaven.test.skip=true,如果网络好的话,很快就能编译成功。
  4. 使用IDEA打开源代码。

02 开发代码

在开发pinpoint插件之前,需要阅读一下官方文档中的pinpoint插件开发指南。但是我们现在要开发的log4j2的插件功能相对简单,从上述描述中,可以知道,主要就是将pinpoint调用链相关信息输出到日志中。

首先我们查看一下,log4j和logback的插件是如何开发的,我们模仿一下即可。主要包括一下几点:

  • 在plugins模块下创建log4j2的maven子模块,创建完成如下图:


    log4j2-module
  • 在pom中添加log4j2的依赖:

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.10.0</version>
    <scope>provided</scope>
</dependency>
  • 找到代码插入点 org.apache.logging.log4j.ThreadContext
  • 在拦截器中,将需要的信息插入到其中。
  • 这样信息就到了log的 MDC 中。

完整的代码参见我的github。log4j2的插件代码参考官方github中的一个issue问题。

03 编译

在编译之前需要注意几点:

  • 在plugins的pom文件中添加log4j2的maven坐标:
<dependency>
    <groupId>com.navercorp.pinpoint</groupId>
    <artifactId>pinpoint-log4j2-plugin</artifactId>
    <version>${project.version}</version>
</dependency>
  • 修改编译的jdk版本,由jdk1.6升级为jdk1.7,原因是我们依赖的log4j2的jar要求jdk的版本是1.7+,在总的pom文件中修改(操作系统的环境变量JAVA_7_HOME要存在):
<jdk.version>1.7</jdk.version>
<jdk.home>${env.JAVA_7_HOME}</jdk.home>
  • 升级了jdk版本之后,编译会发现,pinpoint中有个模块是依赖jdk1.6的,就是 pinpoint-profiler-optional-jdk6,它是一个可选的模块,于是需要我们在编译的是排除掉。

这样最后我们编译的命令就是:mvn -pl '!com.navercorp.pinpoint:pinpoint-profiler-optional-jdk6' clean install -Dmaven.test.skip=true,不出意外我们应该能编译成功。

最后编译完成之后,可以在agent模块的target目录下找到我们需要的最新的agent包,拿走替换原来的agent即可实现支持log4j2了,当然需要在pinpoint.config中需要配置开启:profiler.log4j2.logging.transactioninfo=true

04 结束

到此为止,完成了第一个pinpoint插件的开发,相对比较简单,继续深入学习pinpoint的实现原理,希望能根据项目需求开发更多的插件,以实现我们全链路连贯性监控的目标。

推荐阅读更多精彩内容