pinpoint插件开发之一:牛刀小试,调整gson插件

从本章开始我们一起来实战pinpoint插件开发,做一些实用的pinpoint插件,本着先易后难的原则,我们从修改现有插件开始吧;

准备工作

  1. 本次实战的操作环境是win10专业版,安装了Docker Community Edition Version 17.09.0-ce-win33(13620);
  2. 编译环境,请参照《Docker下,极速体验编译pinpoint1.6.x分支》,我们会用这个容器编译构建修改过的插件,为了复制文件方便,我们启动容器使用以下命令:
docker run --name=ppcompile001 -p 19003:22 -idt -v c:/share:/usr/Downloads bolingcavalry/jdk7-mvn339-pinpoint16x-compile:0.0.1

参数-v c:/share:/usr/Downloads表示将当前电脑的c:/share目录和容器的/usr/Downloads目录建立映射,这样我们就能把容器里的文件复制出来了(记得先在c盘根目录下创建share目录); 3. pinpoint的运行环境,请参照《Docker下,极速体验pinpoint1.6.3》,启动了三个容器,一个是pinpoint的web server,还有两个是运行着web应用的tomcat,运行环境运行成功后,我们可以在pinpoint的web server上看到web应用的服务调用链,为了复制文件方便,我们将docker-compose.yml文件中的每个容器也加上目录映射参数,整个docker-compose.yml内容如下:

version: '2'
services:
  pinpoint-server: 
    image: bolingcavalry/centos67-hbase126-pinpoint163-server:0.0.1
    container_name: pinpoint-server
    ports:
      - "19001:22"
      - "28080:28080"
    volumes:
      - c:/share:/usr/Downloads
    restart: always
  tomcat001: 
    image: bolingcavalry/centos67-pinpoint163-agent:0.0.1
    container_name: tomcat001
    links: 
      - pinpoint-server:pinpointhost
    ports:
      - "8081:8080"
    environment:
      TOMCAT_SERVER_ID: tomcat001
      PINPOINT_AGENT_ID: ppagent20171105001 
      PINPOINT_APPLICATION_NAME: app20171105001
    volumes:
      - c:/share:/usr/Downloads
    restart: always 
  tomcat002: 
    image: bolingcavalry/centos67-pinpoint163-agent:0.0.1
    container_name: tomcat002
    depends_on:
      - tomcat001
    links: 
      - pinpoint-server:pinpointhost
    ports:
      - "8082:8080"
    environment:
      TOMCAT_SERVER_ID: tomcat002
      PINPOINT_AGENT_ID: ppagent20171105002 
      PINPOINT_APPLICATION_NAME: app20171105002
    volumes:
      - c:/share:/usr/Downloads
    restart: always 

可以看到每个容器的配置中都有volumes参数,对容器和当前电脑的目录做了映射;

pinpoint对Gson类的监控

在之前的《Docker下,极速体验pinpoint1.6.3》一文中,我们在tomcat上部署了一个web应用,里面有这么一段代码:

public String tracegson(HttpServletRequest request, Model model) {
        String name = get(request, "name");
        String age = get(request, "age");

        Student student = new Student();
        student.setName(name);
        student.setAge(Integer.valueOf(age));

        Gson gson = new Gson();

        String parseStr = gson.toJson(student, Student.class);

        logger.info("gson str [{}]", parseStr);

        return String.format("gson str : %s [%s]", parseStr, tag());
    }

上面的代码中用到了Gson类的toJson方法,由于pinpoint1.6.3是带有Gson插件的,所以执行此方法后在pinpoint的调用链跟踪列表中可以看到对toJson方法的调用,如下图,至于如何部署和执行这段代码,请参照《Docker下,极速体验pinpoint1.6.3》

上图底部的红框中是对Gson的toJson方法的监控,可以看到这个节点已经不能展开了,难道gson插件不输出一些参数么?是本来就没有参数?还是参数没有显示?

分析源码

去pinpoint的github上看看,地址:https://github.com/naver/pinpoint; 切到1.6.x分支,在plugin目录下可以看见gson插件的工程,如下图红框所示:

先看看实现这个插件拦截了Gson的toJson方法后做了什么,做的事情都封装在ToJsonInterceptor.java中:

@Override
    public void before(Object target) {
        if (logger.isDebugEnabled()) {
            logger.beforeInterceptor(target, null);
        }

        final Trace trace = traceContext.currentTraceObject();
        if (trace == null) {
            return;
        }

        trace.traceBlockBegin();
    }

上面的代码是Gson的toJson方法执行前做的事情:开启了一次追踪,我们再来看看Gson的toJson方法执行结束后做了什么:

 @Override
    public void after(Object target, Object result, Throwable throwable) {
        if (logger.isDebugEnabled()) {
            logger.afterInterceptor(target, null, result, throwable);
        }

        final Trace trace = traceContext.currentTraceObject();
        if (trace == null) {
            return;
        }

        try {
            SpanEventRecorder recorder = trace.currentSpanEventRecorder();
            recorder.recordServiceType(GsonPlugin.GSON_SERVICE_TYPE);
            recorder.recordApi(descriptor);
            recorder.recordException(throwable);

            if (result != null && result instanceof String) {
                recorder.recordAttribute(GsonPlugin.GSON_ANNOTATION_KEY_JSON_LENGTH, ((String) result).length());
            }
        } finally {
            trace.traceBlockEnd();
        }
    }

注意这一句代码:

recorder.recordAttribute(GsonPlugin.GSON_ANNOTATION_KEY_JSON_LENGTH, ((String) result).length());

这表示toJson方法的返回值如果非空并且是String对象的时候,pinpoint会记录一个参数GsonPlugin.GSON_ANNOTATION_KEY_JSON_LENGTH,这个参数的值是toJson方法返回的字符串的长度;

已经记录了参数,但在pinpoint页面上却没有展示出来,我们去看看这个参数的定义吧:

public static final AnnotationKey GSON_ANNOTATION_KEY_JSON_LENGTH = AnnotationKeyFactory.of(9000, "gson.json.length");

GSON_ANNOTATION_KEY_JSON_LENGTH 是通过AnnotationKeyFactory.of方法创建的,我们跟踪这个方法,最终进入DefaultAnnotationKey的构造方法:

DefaultAnnotationKey(int code, String name, AnnotationKeyProperty... properties) {
        this.code = code;
        this.name = name;

        boolean viewInRecordSet = false;
        boolean errorApiMetadata = false;

        for (AnnotationKeyProperty property : properties) {
            switch (property) {
            case VIEW_IN_RECORD_SET:
                viewInRecordSet = true;
                break;
            case ERROR_API_METADATA:
                errorApiMetadata = true;
                break;
            }
        }

        this.viewInRecordSet = viewInRecordSet;
        this.errorApiMetadata = errorApiMetadata;
    }

上述代码表示,如果我们不传入VIEW_IN_RECORD_SET,那么viewInRecordSet就一直为false,这就是导致gson.json.length参数在pinpoint中不显示的原因,要想让gson.json.length显示出来,只要在AnnotationKeyFactory.of方法的入参中加入VIEW_IN_RECORD_SET即可,接下来我们开始动手修改吧;

在编译环境修改源码

在命令行执行以下命令登录到编译环境的容器中:

docker exec -it ppcompile001 /bin/bash

修改这个文件:

/usr/local/work/pinpoint-1.6.x/plugins/gson/src/main/java/com/navercorp/pinpoint/plugin/gson/GsonPlugin.java

找到下面这行代码:

public static final AnnotationKey GSON_ANNOTATION_KEY_JSON_LENGTH = AnnotationKeyFactory.of(9000, "gson.json.length");

改成下面这样,AnnotationKeyFactory.of方法的入参从两个变成三个了:

public static final AnnotationKey GSON_ANNOTATION_KEY_JSON_LENGTH = AnnotationKeyFactory.of(9000, "gson.json.length", com.navercorp.pinpoint.common.trace.AnnotationKeyProperty.VIEW_IN_RECORD_SET);

修改完毕后保存文件,进入pinpoint工程目录/usr/local/work/pinpoint-1.6.x,执行以下命令开始编译:

mvn install -Dmaven.test.skip=true -e

编译完成后进入以下目录:

/usr/local/work/pinpoint-1.6.x/plugins/gson/target

可以见到最新构建的gson插件,如下图黄框所示:

把这个文件复制到/usr/Downloads目录,由于pinpoint运行环境的三个容器也建立了自己的/usr/Downloads和c:/share目录的映射,所以它们也能立即访问这个文件了;

替换pinpoint server的gson插件

  1. 执行命令docker exec -it pinpoint-server /bin/bash进入pinpoint server容器;
  2. 执行以下命令替换原有的gson插件:
rm -f /usr/local/work/tomcat-collector/apache-tomcat-8.0.36/webapps/ROOT/WEB-INF/lib/pinpoint-gson-plugin-1.6.3-SNAPSHOT.jar

cp /usr/Downloads/pinpoint-gson-plugin-1.6.3-SNAPSHOT.jar /usr/local/work/tomcat-collector/apache-tomcat-8.0.36/webapps/ROOT/WEB-INF/lib/

rm -f /usr/local/work/tomcat-web/apache-tomcat-8.0.36/webapps/ROOT/WEB-INF/lib/pinpoint-gson-plugin-1.6.3-SNAPSHOT.jar

cp /usr/Downloads/pinpoint-gson-plugin-1.6.3-SNAPSHOT.jar /usr/local/work/tomcat-web/apache-tomcat-8.0.36/webapps/ROOT/WEB-INF/lib/

以上命令将pinpoint-collector和pinpoint-web两个容器的gson插件先删除,再把我们放在c:/share目录下最新的gson插件复制过去; 3. 重启collector和web两个应用,执行以下命令:

/usr/local/work/tomcat-collector/apache-tomcat-8.0.36/bin/shutdown.sh
/usr/local/work/tomcat-web/apache-tomcat-8.0.36/bin/shutdown.sh
/usr/local/work/tomcat-collector/apache-tomcat-8.0.36/bin/startup.sh
/usr/local/work/tomcat-web/apache-tomcat-8.0.36/bin/startup.sh

替换tomcat001容器的gson插件

  1. 执行命令docker exec -it tomcat001 /bin/bash进入tomcat001容器;
  2. 执行以下命令替换原有的gson插件:
rm -f /usr/local/work/pinpoint-agent-1.6.3/plugin/pinpoint-gson-plugin-1.6.3-SNAPSHOT.jar
cp /usr/Downloads/pinpoint-gson-plugin-1.6.3-SNAPSHOT.jar /usr/local/work/pinpoint-agent-1.6.3/plugin/

以上命令将pinpoint agent目录下的gson插件先删除,再把我们放在c:/share目录下最新的gson插件复制过去; 3. 重启,由于tomcat是随着容器一起启动的,此处如果用shutdown.sh命令停止tomcat服务会导致容器退出,所以我们直接重启tomcat001容器吧; 4. 执行exit退出容器; 5. 在当前电脑的命令行执行docker restart tomcat001重启容器;

验证新的插件

  1. 验证前,请确保web应用已经按照《Docker下,极速体验pinpoint1.6.3》中的方式部署到了tomcat001和tomcat002上面;
  2. 浏览器访问:http://localhost:8081/pinpointtracedemo/tracegson?name=tom&age=11,这次访问会有一次Gson的toJson方法调用;
  3. 通过访问pinpoint-server,看刚刚那次请求的追踪信息,如下图:

toJson这个节点现在可以展开了,红框中就是新的节点,内容是我们修改过的gson.json.length参数,值是23;

小结

至此,我们的第一次插件开发实战就结束了,小结本次插件开发过程: 1. 修改插件源码; 2. 编译构建成新的包; 3. 新包替换pingpoint server上的collector和web应用中的旧包; 4. 重启pinpoint server上的collector和web server; 5. 新包替换pinpoint agent上的旧包; 6. 重启agent上的业务的web server;

以上只是修改了原有插件,接下来的实战中,我们一起创建一个全新的插件,实现我们需要的功能;

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Python

Linux权限详解 命令之 chmod:修改权限

在这种使用方式中,首先我们需要了解数字如何表示权限。 首先,我们规定 数字 4 、2 和 1表示读、写、执行权限(具体原因可见下节权限详解内容),即 r=4,w...

542
来自专栏晨星先生的自留地

攻破VulnOS(3)之挑战PwnLad

2596
来自专栏北京马哥教育

史上最全Linux命令科普 | 常见命令的名称是怎么来的?

1 目录缩写 缩写 | 全称 | 说明 --- | --- | --- | /bin | BINaries | 二进制可执行命令 /dev | DEVices...

2664
来自专栏张善友的专栏

Servant:基于Web的IIS管理工具

Servant for IIS是个管理IIS的简单、自动化的Web管理工具。安装Servant的过程很简单,只要双击批处理文件Install Servant S...

19210
来自专栏施炯的IoT开发专栏

Serial Port Debug on EBox4300

  自从上次安装了EBox4300的开发环境以后,就忙着其他事情,没有来得及做点应用。今天做了一个串口通信的应用,发上来和大家分享一下。   串口是设备调试中经...

1876
来自专栏云端漫步

harbor源码分析之配置文件生成脚本解析(三)

在harbor第二篇中介绍了安装脚本install.sh,在一个脚本中有一step是执行prepare脚本.这个脚本主要是用来生成配置文件的.在本篇中,将对这一...

301
来自专栏张戈的专栏

菜单式Shell运维脚本调试小记

今天上午,同事叫我帮他看下他正在写的脚本到底哪里出了问题,执行总是达不到预期功能。于是 ssh 连过去看能否发现问题,经过多次 Debug 定位与排除,问题终于...

3132
来自专栏前端安全

前端调试入门

这里的控制台特指PC端浏览器进入开发者模式之后新打开的操作界面。常见的控制台有Chrome的控制台,Firefox的firebug。这些都能帮助我们调试前端问题...

60733
来自专栏杨建荣的学习笔记

Linux中的configure,make,make install到底在做些什么

在Linux下经常要安装部署一些软件包或者工具,拿到安装包之后一看,简单,configure,make, make install即可搞定。 有时...

3283
来自专栏云计算教程系列

如何在Ubuntu 16.04上使用Git Hooks部署Jekyll站点

Jekyll是一个静态站点生成器,它提供了内容管理系统(CMS)的一些优点,同时避免了此类数据库驱动的站点引入的性能和安全问题。它具有“博客意识”,并包含处理日...

1053

扫码关注云+社区