• pinpoint插件开发实践


    plugin基本结构

    一个plugin主要由三部分构成,插件类增强定义(ProfilerPlugin接口实现)、插件描述定义(TraceMetadataProvider接口实现)、增强类拦截器实现(AroundInterceptor接口实现)

    举个栗子

    1、插件定义

    ProfilerPlugin 接口只有一个setup方法,插件加载时会调用setup方法,一般我们会在这个时候对指定的类进行增强。同时一般还会实现TransformTemplateAware接口,通过这个接口可以拿到TransformTemplate对象,对类进行增强主要是通过这个类。

    public class OpenSearchPlugin implements ProfilerPlugin, TransformTemplateAware {
    
        private TransformTemplate transformTemplate;
        @Override
        public void setup(ProfilerPluginSetupContext context) {
            OpenSearchConfig config = new OpenSearchConfig(context.getConfig());
            if (!config.isEnable()) {
                return;
            }
            addTransformers();
        }
        private void addTransformers() {
            transformTemplate.transform("com.aliyun.opensearch.CloudsearchClient", new TransformCallback() {
                @Override
                public byte[] doInTransform(Instrumentor instrumentor, ClassLoader classLoader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException {
                    InstrumentClass target = instrumentor.getInstrumentClass(classLoader, className, classfileBuffer);
                    InstrumentMethod method = target.getDeclaredMethod("call","java.lang.String","java.util.Map","java.lang.String","boolean","java.lang.StringBuffer");
                    method.addInterceptor("com.navercorp.pinpoint.plugin.opensearch.interceptor.OpenSearchInterceptor");
                    return target.toBytecode();
                }
            });
        }
        @Override
        public void setTransformTemplate(TransformTemplate transformTemplate) {
            this.transformTemplate = transformTemplate;
        }
    }
    

      上面这个例子,我们对CloudsearchClient类进行了增强,具体增强的是call方法,最后指定了对应的拦截器OpenSearchInterceptor

    通过ProfilerPluginSetupContext.getConfig()可以拿到我们在pinpoint.config中的配置。

    2、插件描述定义

    public class OpenSearchTypeProvider implements TraceMetadataProvider{
        @Override
        public void setup(TraceMetadataSetupContext context) {
            context.addServiceType(OpenSearchConstant.OPEN_SEARCH_SERVICE, AnnotationKeyMatchers.ARGS_MATCHER);
            context.addAnnotationKey(OpenSearchConstant.SEARCH_INDEX_NAME);
            context.addAnnotationKey(OpenSearchConstant.SEARCH_QUERY);
        }
    }

    这里指定了插件的服务名称为OPEN_SEARCH_SERVICE,增加了两个参数:SEARCH_INDEX_NAME和SEARCH_QUERY,这个主要是在链路详情中显示自定义的参数

    这里如果不配置,在web页面上是没办法显示的。

    3、拦截器实现

    public class OpenSearchInterceptor implements AroundInterceptor {
    
        private static final String    OPEN_SEARCH = "openSearch";
        private final MethodDescriptor descriptor;
        private final TraceContext     traceContext;
    
        public OpenSearchInterceptor(TraceContext traceContext, MethodDescriptor descriptor){
            this.descriptor = descriptor;
            this.traceContext = traceContext;
        }
    
        private boolean getWwitch() {
            String applicationName = traceContext.getApplicationName();
            if (!traceContext.collectSwitch(applicationName, OPEN_SEARCH, null)) {
                return false;
            }
            return true;
        }
    
        @Override
        public void before(Object target, Object[] args) {
            if (!getWwitch()) {
                return;
            }
    
            Trace trace = traceContext.currentTraceObject();
            if (trace == null) return;
            SpanEventRecorder recorder = trace.traceBlockBegin();
            recorder.recordServiceType(OpenSearchConstant.OPEN_SEARCH_SERVICE);
    
        }
    
        @Override
        public void after(Object target, Object[] args, Object result, Throwable throwable) {
            if (!getWwitch()) {
                return;
            }
            Trace trace = traceContext.currentTraceObject();
            if (trace == null) return;
            try {
                // String path = (String) args[0];
                Map<String, String> param = (Map<String, String>) args[1];
                // String method= (String) args[2];
                // Boolean isPb= (Boolean) args[3];
                // StringBuffer sb= (StringBuffer) args[4];
                SpanEventRecorder recorder = trace.currentSpanEventRecorder();
                // String format=param.get("format");
                String indexName = param.get("index_name");
                String query = param.get("query");
                recorder.recordApi(descriptor, new Object[] { indexName });
                recorder.recordException(throwable);
                recorder.recordAttribute(OpenSearchConstant.SEARCH_INDEX_NAME, indexName);
                recorder.recordAttribute(OpenSearchConstant.SEARCH_QUERY, query);
                recorder.recordServiceType(OpenSearchConstant.OPEN_SEARCH_SERVICE);
                recorder.recordDestinationId(OpenSearchConstant.OPEN_SEARCH_DESTINATION);
                // recorder.recordAttribute(AnnotationKey.ARGS0,indexName);
    
                if (target instanceof BaseUriGetter) {
                    String endPoint = ((BaseUriGetter) target)._$PINPOINT$_getBaseURI();
                    recorder.recordEndPoint(endPoint);
                }
            } finally {
                trace.traceBlockEnd();
            }
        }
    }

     拦截器的实现主要是一个before和after方法,对应我们的方法执行前和执行后。

    通过SpanEventRecorder可以写入一条链路详情到调用链中。

    附上插件类图:

  • 相关阅读:
    go 排序sort的使用
    MySQL 创建索引
    jenkins 解决构建成功后进程消失的问题
    go redigo的简单操作
    go 实现struct转map
    MySQL写入用户微信名
    MySQL简单优化
    python 定时修改数据库
    go 实现简单的加权分配
    python 遇到的小坑
  • 原文地址:https://www.cnblogs.com/yissheng/p/9971924.html
Copyright © 2020-2023  润新知