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可以寫入一條鏈路詳情到調用鏈中。
附上插件類圖: