pinpoint插件开发之二:从零开始新建一个插件

简介: 从零开始新建pinpoint插件,本篇给出从编码到部署运行的详细步骤

欢迎访问我的GitHub

这里分类和汇总了欣宸的全部原创(含配套源码): https://github.com/zq2599/blog_demos

本篇概览

开发pinpoint的经验小结

  • 对初学者来说,从最基础的pinpoint体验,到个性化插件开发,应该是个逐步学习的过程,简单来说分为以下步骤:
  1. 对pinpoint整体功能、pinpoint server和pinpoint agent有简单的了解;
  2. 学会编译构建pinpoint;
  3. 对pinpoint 插件的部署有简单的了解;
  4. 实战插件开发;
  • 所以建议您按照以下步骤来逐步实践:
  1. 《Docker下,极速体验编译pinpoint1.6.x分支》
  2. 《Docker下,极速体验pinpoint1.6.3》;
  3. 《pinpoint插件开发之一:牛刀小试,调整gson插件》;
  4. 本章,开发一个全新的插件;
  • 建议您先快速浏览上述三篇文章,然后咱们再一起动手从零开始做一个完成的插件,并在web应用中体验这个插件的功能;

新插件的功能

  • 新做的插件用来做什么呢?
  • 实际业务的生产环境中,常通过日志查看一些程序运行时的信息,例如把用户id打印到日志中,所以我打算做个插件将这些信息在pinpoint上显示出来;

对业务代码的侵入性

  • 为了避免这个功能导致大量修改已有业务代码(侵入性),我的方法是拦截sl4j日志的info这个方法,也就是ch.qos.logback.classic.Logger类的info方法,把Logger.info(String str)的str参数在pinpoint输出;

特殊的约定

  • 生产环境中到处都调用了sl4j的info方法,如果全部拦截内容就太多了,所以和业务做个约定,info方法入参的字符串,如果以pinpoint_bizlog_为前缀,那么我们的插件才做拦截,将日志的信息打印出来,其他的保持原样;
  • 这样只要业务执行诸如logger.info("pinpoint_bizlog_" + "userid :" + userid);这样的代码,我们的插件就会将userid : xxx这样的字符串在pinpoint的追踪信息中显示出来;

开始吧

  • 功能已经设计好了,那我们就开始吧,给插件起个名字:bizlog

下载pinpoint源码

这里写图片描述

导入到ide

  • 整个pinpoint是一个大的maven工程,里面有很多小工程,所以我们用idea来导入;

创建新的maven工程

  • 在plugins文件夹下新建一个bizlog目录,里面新增一个pom.xml文件,内容如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.navercorp.pinpoint</groupId>
        <artifactId>pinpoint</artifactId>
        <relativePath>../..</relativePath>
        <version>1.6.3-SNAPSHOT</version>
    </parent>

    <artifactId>pinpoint-bizlog-plugin</artifactId>
    <name>pinpoint-bizlog-plugin</name>
    <packaging>jar</packaging>

    <dependencies>
        <dependency>
            <groupId>com.navercorp.pinpoint</groupId>
            <artifactId>pinpoint-bootstrap-core</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>
  • 将自己作为pinpoint的子工程,依赖的是pinpoint-bootstrap-core这个库;

通过配置指定插件类和元信息类

  • 需要通过配置文件告诉pinpoint当前插件的功能类和元数据类在哪里,配置文件有两个,都放在src/main/java/resources/META-INF/services目录,如下图:

这里写图片描述

  • 文件名:com.navercorp.pinpoint.bootstrap.plugin.ProfilerPlugin
  • 文件内容:com.navercorp.pinpoint.plugin.bizlog.BizlogPlugin
  • 文件功能:指定插件功能类
  • 文件名:com.navercorp.pinpoint.common.trace.TraceMetadataProvider
  • 文件内容:com.navercorp.pinpoint.plugin.bizlog.BizlogMetadataProvider
  • 文件功能:指定元数据类

开发插件功能类

  • 在src\main\java\com\navercorp\pinpoint\plugin\bizlog目录下创建功能类BizPlugin.java:
package com.navercorp.pinpoint.plugin.bizlog;

import com.navercorp.pinpoint.bootstrap.instrument.InstrumentClass;
import com.navercorp.pinpoint.bootstrap.instrument.InstrumentException;
import com.navercorp.pinpoint.bootstrap.instrument.InstrumentMethod;
import com.navercorp.pinpoint.bootstrap.instrument.Instrumentor;
import com.navercorp.pinpoint.bootstrap.instrument.MethodFilters;
import com.navercorp.pinpoint.bootstrap.instrument.transformer.TransformCallback;
import com.navercorp.pinpoint.bootstrap.instrument.transformer.TransformTemplate;
import com.navercorp.pinpoint.bootstrap.instrument.transformer.TransformTemplateAware;
import com.navercorp.pinpoint.bootstrap.logging.PLogger;
import com.navercorp.pinpoint.bootstrap.logging.PLoggerFactory;
import com.navercorp.pinpoint.bootstrap.plugin.ProfilerPlugin;
import com.navercorp.pinpoint.bootstrap.plugin.ProfilerPluginSetupContext;
import com.navercorp.pinpoint.common.trace.AnnotationKey;
import com.navercorp.pinpoint.common.trace.AnnotationKeyFactory;
import com.navercorp.pinpoint.common.trace.ServiceType;
import com.navercorp.pinpoint.common.trace.ServiceTypeFactory;

import java.security.ProtectionDomain;

/**
 * @author willzhao
 */
public class BizlogPlugin implements ProfilerPlugin, TransformTemplateAware {
    //BIZLOG_SERVICE_TYPE是bizlog插件的身份定义,用了1998这个id
    public static final ServiceType BIZLOG_SERVICE_TYPE = ServiceTypeFactory.of(1998, "BIZLOG");

    //BIZLOG_ANNOTATION_KEY_INFO是打算在pinpoint追踪信息中显示的属性的定义,用了9998这个id
    public static final AnnotationKey BIZLOG_ANNOTATION_KEY_INFO = AnnotationKeyFactory.of(9998, "bizlog.info", com.navercorp.pinpoint.common.trace.AnnotationKeyProperty.VIEW_IN_RECORD_SET);

    private static final String BIZLOG_SCOPE = "BIZLOG_SCOPE";

    private final PLogger logger = PLoggerFactory.getLogger(this.getClass());

    private TransformTemplate transformTemplate;

    @Override
    public void setup(ProfilerPluginSetupContext context) {
            //Logger类被加载的时候,会注入这里new的TransformCallback,对这个类的实例在线程中的行为进行拦截
            transformTemplate.transform("ch.qos.logback.classic.Logger", new TransformCallback() {

                @Override
                public byte[] doInTransform(Instrumentor instrumentor, ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException {
                    InstrumentClass target = instrumentor.getInstrumentClass(loader, className, classfileBuffer);
                    
                    //找到所有名为info的方法
                    for (InstrumentMethod m : target.getDeclaredMethods(MethodFilters.name("info"))) {
                        ////注入Interceptor,在Logger类的实例执行info方法的时候会执行这个interceptor
                        m.addScopedInterceptor("com.navercorp.pinpoint.plugin.bizlog.interceptor.BizlogInterceptor", BIZLOG_SCOPE);
                    }

                    return target.toBytecode();
                }
            });
    }


    @Override
    public void setTransformTemplate(TransformTemplate transformTemplate) {
        this.transformTemplate = transformTemplate;
    }
}
  • 以上方法对ch.qos.logback.classic.Logger类就行了注入,在Logger类的实例的info方法被调用时注入的Interceptor就会被执行;

开发元信息类

  • 在src\main\java\com\navercorp\pinpoint\plugin\bizlog目录下创建元信息类BizlogMetadataProvider.java:
package com.navercorp.pinpoint.plugin.bizlog;

import com.navercorp.pinpoint.common.trace.TraceMetadataProvider;
import com.navercorp.pinpoint.common.trace.TraceMetadataSetupContext;

/**
 * @author willzhao
 */
public class BizlogMetadataProvider implements TraceMetadataProvider {
    /**
     * @see TraceMetadataProvider#setup(TraceMetadataSetupContext)
     */
    @Override
    public void setup(TraceMetadataSetupContext context) {
        //设定当前插件的ServiceType,既插件的唯一身份
        context.addServiceType(BizlogPlugin.BIZLOG_SERVICE_TYPE);
        //设定当前插件要展示的参数
        context.addAnnotationKey(BizlogPlugin.BIZLOG_ANNOTATION_KEY_INFO);
    }
}
  • 以上方法会被pinpoint调用,这样pinpoint就知道了我们这次新增的这个插件了,以及我们要在pinpoint中显示的参数;

拦截器BizlogInterceptor

  • 拦截器是Logger类被加载的时候被pinpoint注入的,被拦截的方法在执行前后所做的事情都在拦截器中定义,以下就是BizlogInterceptor:
package com.navercorp.pinpoint.plugin.bizlog.interceptor;

import com.navercorp.pinpoint.bootstrap.context.MethodDescriptor;
import com.navercorp.pinpoint.bootstrap.context.SpanEventRecorder;
import com.navercorp.pinpoint.bootstrap.context.Trace;
import com.navercorp.pinpoint.bootstrap.context.TraceContext;
import com.navercorp.pinpoint.bootstrap.interceptor.AroundInterceptor;
import com.navercorp.pinpoint.bootstrap.logging.PLogger;
import com.navercorp.pinpoint.bootstrap.logging.PLoggerFactory;
import com.navercorp.pinpoint.plugin.bizlog.BizlogPlugin;

/**
 * logger info method interceptor
 *
 * @author willzhao
 */
public class BizlogInterceptor implements AroundInterceptor {
    private final TraceContext traceContext;
    private final MethodDescriptor descriptor;
    private final PLogger logger = PLoggerFactory.getLogger(getClass());

    public BizlogInterceptor(TraceContext traceContext, MethodDescriptor descriptor) {
        this.traceContext = traceContext;
        this.descriptor = descriptor;
    }

    private static boolean shouldTrace(Object[] args){
        return null!=args
                && args.length>0
                && (args[0] instanceof String)
                && ((String)args[0]).indexOf("pinpoint_bizlog_name")>-1;
    }

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

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

        if(!shouldTrace(args)){
            return;
        }

        trace.traceBlockBegin();

    }

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

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

        if(!shouldTrace(args)){
            return;
        }

        try {
            SpanEventRecorder recorder = trace.currentSpanEventRecorder();
               recorder.recordServiceType(BizlogPlugin.BIZLOG_SERVICE_TYPE);
            recorder.recordApi(descriptor);
            recorder.recordException(throwable);
            recorder.recordAttribute(BizlogPlugin.BIZLOG_ANNOTATION_KEY_INFO, args[0]);
        } finally {
            trace.traceBlockEnd();
        }
    }
}
  • 上述的代码中,before和after方法分别代表logger.info方法执行前和执行后拦截器所做的事情,shouldTrace方法检查入参中是否有"pinpoint_bizlog_name"前缀,如果没有就不执行拦截操作了,如果有,就执行trace操作,recorder.recordAttribute会将入参记录并在pinpoint追踪信息中展示出来;

plugins工程的配置

  • 由于我们新建的bizlog工程和其他插件工程一样是plugins的子工程,为了能构建和打包,要在plugins工程中配置,打开plugins文件夹下的pom.xml文件:
  • 首先,在modeles节点中增加以下内容:
<module>bizlog</module>

然后,在dependencies节点增加以下内容:

<dependency>
    <groupId>com.navercorp.pinpoint</groupId>
    <artifactId>pinpoint-bizlog-plugin</artifactId>
    <version>${project.version}</version>
</dependency>

开发小结

  • 除了修改plugins目录下的pom.xml文件,本次开发的插件一共需要新增六个文件:
  1. 插件的pom.xml;
  2. com.navercorp.pinpoint.bootstrap.plugin.ProfilerPlugin:定义插件功能类
  3. com.navercorp.pinpoint.common.trace.TraceMetadataProvider:定义插件元信息类
  4. BizlogPlugin.java:插件功能类,确定对哪个类的哪些方法做拦截
  5. BizlogMetadataProvider.java:元信息类:确定插件id和插件参数id
  6. BizlogInterceptor.java:拦截类:定义在拦截的时候做什么事情

bizlog插件源码下载

  • bizlog的源码可以在我的git下载,地址是:git@github.com:zq2599/pinpoint163-plugin-develop.git
  • 这里面包含了完整的pinpoint1.6.X分支的源码,bizlog的在plugins目录下,如下图红框所示:

这里写图片描述

编译构建bizlog插件的环境

  • 开发已经完成,接下来就是编译构建bizlog插件了,推荐在Docker环境去构建,在Docker构建pinpoint插件的方法请参照《Docker下,极速体验编译pinpoint1.6.x分支》,和文中略有区别的是,为了方便复制文件我们用以下命令来启动容器(多了个-v参数):
docker run --name=ppcompile001 -p 19003:22 -idt -v c:/share:/usr/Downloads bolingcavalry/jdk7-mvn339-pinpoint16x-compile:0.0.1
  • 这样启动后,当前电脑的c:/share目录和容器的/usr/Downloads目录实际上是同一个位置了;

开始编译构建

  • 编译pinpoint插件的Docker环境准备好后,我们把bizlog插件相关的内容都移植过来吧:
  1. 像上面那样修改ppcompile001容器中plugins目录下的pom.xml文件,给节点和节点增加内容;
  2. 将前面做好的bizlog目录整体复制到ppcompile001容器中的plugins目录下;
  3. 在pinpoint目录下执行编译命令:mvn install -Dmaven.test.skip=true -e
  4. 编译成功后,在bizlog/target目录下可以看到最新的插件,如下图红色字体所示:

这里写图片描述

准备pinpoint环境

  • 为了验证bizlog插件,我们要有包含以下功能的环境:
  1. 有pinpoint server;
  2. 有pinpoint agent;
  3. pinpoint agent上部署了web应用,能被pinpoint追踪;
  • 如何快速准备好这样一套环境呢?请参照《Docker下,极速体验pinpoint1.6.3》一文,能够以最快速度将pinpoint server和pinpoint agent搭建好,然后把web应用部署到pinpoint agent上;

部署bizlog插件

  1. 进入pinpoint server容器,在pinpoint-collector和pinpoint-web两个tomcat server的apache-tomcat-8.0.36/webapps/ROOT/WEB-INF/lib/目录下,放置bizlog的jar包;
  2. 重启collector和web两个tomcat;
  3. 进入tomcat001容器,在pinpoint-agent-1.6.3/plugin/目录下放置bizlog的jar包;
  4. 重启tomcat001容器;

验证bizlog插件

  • 部署在tomcat001上的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());
    }
  • 为了验证bizlog插件,在方法return之前加了下面这两句,两次执行logger.info方法,第一次带上了pinpoint_bizlog_name前缀,第二次没有带:
logger.info("pinpoint_bizlog_name 1. [" + name + "], age [" + age + "]");

logger.info("2. [" + name + "], age [" + age + "]");
  • 加上之后,将web应用部署到tomcat001容器上,访问地址:http://localhost:8081/pinpointtracedemo/tracegson?name=tom&age=11
  • 然后去pinpoint上看一下,如下图:

这里写图片描述

  • 可以看到我们的插件已经出现在红框位置,而且只将“pinpoint_bizlog_name”前缀的log信息打印出来,今后需要通过pinpoint追踪的信息,都可以通过logger.info("pinpoint_bizlog_xxxxxx的方式来实现;
  • 以上就是开发一个完整插件的过程,希望能对您有所帮助,也祝您顺利开发出自己需要的插件;

了解pinpoint编译环境的更多细节

了解pinpoint server、pinpoint agent部署的更多细节

欢迎关注阿里云开发者社区博客:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...
相关实践学习
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
相关文章
|
SQL XML 关系型数据库
Mybatis-Plus通过SQL注入器实现真正的批量插入
Mybatis-Plus通过SQL注入器实现真正的批量插入
8118 0
Mybatis-Plus通过SQL注入器实现真正的批量插入
|
存储 编解码 缓存
webgl系列之抗锯齿和深度缓存
前言 大家好我是Fly 哥, 这是今年webgl 系列的第三篇文章, 如果你之前的两篇文章没看的话,建议先看一下,然后再来看这一篇文章 Webgl 系列之buffer的使用 webgl系列之对光栅化的理解 上一篇文章,任何虚拟3维世界的转换到二维屏幕中通过「采样」 也就判断屏幕上的每个像素中心点是不是在三角形内部的得到了 下面这幅图: 图片 走样之前 这时候有同学问, 这不像三角形哇, 这个其实用个专业的词—— 「锯齿」 , 我的理解 一个三角形经过光栅化后, 得到屏幕上每一个像素点 组成的像素点的集合。那到底是经过什么样的处理得到下面这张图: 图片 final 反走样 其实出现上面
webgl系列之抗锯齿和深度缓存
|
监控 应用服务中间件 Shell
pinpoint插件开发之一:牛刀小试,调整gson插件
pinpoint自定义插件开发起步,先拿已有插件做微调,把基本套路摸清楚
702 0
pinpoint插件开发之一:牛刀小试,调整gson插件
|
JSON 移动开发 应用服务中间件
调用Feign接口报错:JSON parse error:Illegal character ((CTRL-CHAR, code 31))
调用Feign接口报错:JSON parse error:Illegal character ((CTRL-CHAR, code 31))
4186 0
调用Feign接口报错:JSON parse error:Illegal character ((CTRL-CHAR, code 31))
|
4月前
|
缓存 监控 测试技术
全链路压测实施指南
全链路压测是保障分布式系统稳定的核心手段,通过模拟真实流量,覆盖从请求接入到数据存储的完整链路,提前发现性能瓶颈、验证架构与预案。本文从压测规划、数据构造、流量模拟、监控分析、问题定位等十大维度,系统拆解实施流程与实战技巧,结合双11等典型案例,梳理标准化压测流程,助力企业高效落地全链路压测,为大促高峰提供坚实稳定性保障。
457 0
|
5月前
|
人工智能 API 调度
我用 n8n 教自动化,结果自己在干最蠢的活
作者本为学员免费开通n8n账号,却因频繁手动操作陷入效率困境。起初尝试全自动流程,反被滥用;最终引入“人在回路”(HITL)机制,结合自动化与人工审核,用飞书审批实现高效协作。真正高效的自动化,是让机器处理重复工作,人类专注核心决策。
|
10月前
|
机器学习/深度学习 存储 NoSQL
基于 Flink + Redis 的实时特征工程实战:电商场景动态分桶计数实现
本文介绍了基于 Flink 与 Redis 构建的电商场景下实时特征工程解决方案,重点实现动态分桶计数等复杂特征计算。通过流处理引擎 Flink 实时加工用户行为数据,结合 Redis 高性能存储,满足推荐系统毫秒级特征更新需求。技术架构涵盖状态管理、窗口计算、Redis 数据模型设计及特征服务集成,有效提升模型预测效果与系统吞吐能力。
1121 10
|
监控 安全 Linux
【专栏】Linux中六个常用的网络命令:ping、traceroute、netstat、nmap、ifconfig和ip
【4月更文挑战第28天】本文介绍了Linux中六个常用的网络命令:ping、traceroute、netstat、nmap、ifconfig和ip,以及它们在测试网络连通性、追踪路由、查看网络状态、安全扫描和接口配置等场景的应用。通过学习和运用这些命令,系统管理员和网络爱好者能更有效地诊断和管理网络问题,确保网络稳定运行。
1234 0
|
监控 Java Linux
Linux安装pinpoint监控,保姆级安装攻略,没有之一
Linux安装pinpoint监控,保姆级安装攻略,没有之一
797 0
Linux安装pinpoint监控,保姆级安装攻略,没有之一

热门文章

最新文章

下一篇
开通oss服务