pinpoint插件开发
pinpoint下载
git clone https://github.com/naver/pinpoint.git
设置环境变量
export JAVA_6_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home
export JAVA_7_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home
export JAVA_8_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home
export JAVA_9_HOME=/Library/Java/JavaVirtualMachines/jdk-9.0.1.jdk/Contents/Home
注意:pinpoint项目要成功跑起来,需要按官方要求配置“JAVA_6_HOME、JAVA_7_HOME、JAVA_8_HOME、JAVA_9_HOME”等环境变量,故提前安装好需要的jdk
编译
mvn install -Dmaven.test.skip=true -e
Could not resolve dependencies for project com.navercorp.pinpoint:pinpoint-web:war:1.8.2: Could not transfer artifact com.navercorp.pinpoint:pinpoint-rpc:jar:tests:1.8.2 from/to bintray (http://jcenter.bintray.com): Access denied to: http://jcenter.bintray.com/com/navercorp/pinpoint/pinpoint-rpc/1.8.2/pinpoint-rpc-1.8.2-tests.jar , ReasonPhrase:Forbidden. -> [Help 1]
如果报上述错误拉不到jar包,修改pom 中的maven库连接为https://jcenter.bintray.com
新建plugin模块
修改pom
<?xml version="1.0" encoding="UTF-8"?>
<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>
<properties>
<pinpoint.version>1.8.2</pinpoint.version>
</properties>
<groupId>com.never.plugins</groupId>
<artifactId>demo</artifactId>
<packaging>jar</packaging>
<version>1.8.2</version>
<parent>
<groupId>com.navercorp.pinpoint</groupId>
<artifactId>pinpoint</artifactId>
<relativePath>../..</relativePath>
<version>1.8.2</version>
</parent>
<dependencies>
<dependency>
<groupId>com.navercorp.pinpoint</groupId>
<artifactId>pinpoint-bootstrap-core</artifactId>
<scope>provided</scope>
<version>1.8.2</version>
</dependency>
</dependencies>
</project>
新增配置文件
-
com.navercorp.pinpoint.bootstrap.plugin.ProfilerPlugin
文件内容:com.navercorp.pinpoint.plugin.demo.DemoPlugin
文件功能:指定插件功能类 -
com.navercorp.pinpoint.common.trace.TraceMetadataProvider
文件内容:com.navercorp.pinpoint.plugin.bizlog.BizlogMetadataProvider
文件功能:指定元数据类
开发插件功能类
public class DemoPlugin implements ProfilerPlugin, TransformTemplateAware {
//BIZLOG_SERVICE_TYPE是bizlog插件的身份定义,用了1998这个id
public static final ServiceType DEMO_SERVICE_TYPE = ServiceTypeFactory.of(1998, "DEMO");
//BIZLOG_ANNOTATION_KEY_INFO是打算在pinpoint追踪信息中显示的属性的定义,用了9998这个id
public static final AnnotationKey DEMO_ANNOTATION_KEY_INFO = AnnotationKeyFactory
.of(9998, "demo.info", AnnotationKeyProperty.VIEW_IN_RECORD_SET);
private TransformTemplate transformTemplate;
private static final String DEMO_SCOPE = "DEMO_SCOPE";
@Override
public void setTransformTemplate(TransformTemplate transformTemplate) {
this.transformTemplate = transformTemplate;
}
@Override
public void setup(ProfilerPluginSetupContext context) {
System.out.println(">>>>>>>>>>>>>>DemoPlugin.setup>>>>>>>>>>>>>>>>>>>>>>");
//Logger类被加载的时候,会注入这里new的TransformCallback,对这个类的实例在线程中的行为进行拦截
transformTemplate.transform("com.taoche.search.service.impl.AServiceImpl", 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("buildCustomize"))) {
//注入Interceptor,在Logger类的实例执行info方法的时候会执行这个interceptor
m.addScopedInterceptor("com.navercorp.pinpoint.plugin.demo.DemoInterceptor",
DEMO_SCOPE);
}
return target.toBytecode();
}
});
}
}
以上方法对com.taoche.search.service.impl.AServiceImpl
类进行了注入,在AServiceImpl
类的实例的buildCustomize
方法被调用时注入的Interceptorcom.navercorp.pinpoint.plugin.demo.DemoInterceptor
就会被执行;
开发元信息类
public class DemoMetadataProvider implements TraceMetadataProvider {
@Override
public void setup(TraceMetadataSetupContext context) {
System.out.println(">>>>>>>>>>>>>>DemoMetadataProvider.setup>>>>>>>>>>>>>>>>>>>>>>");
context.addServiceType(DemoPlugin.DEMO_SERVICE_TYPE);
context.addAnnotationKey(DemoPlugin.DEMO_ANNOTATION_KEY_INFO);
}
}
以上方法会被pinpoint调用,这样pinpoint就知道了我们这次新增的这个插件了,以及我们要在pinpoint中显示的参数;
拦截器DemoInterceptor
拦截器是AServiceImpl类被加载的时候被pinpoint注入的,被拦截的方法在执行前后所做的事情都在拦截器中定义,以下就是DemoInterceptor:
public class DemoInterceptor implements AroundInterceptor {
private final TraceContext traceContext;
private final MethodDescriptor descriptor;
private final PLogger logger = PLoggerFactory.getLogger(getClass());
public DemoInterceptor(TraceContext traceContext, MethodDescriptor descriptor) {
this.traceContext = traceContext;
this.descriptor = descriptor;
}
@Override
public void before(Object target, Object[] args) {
System.out.println(">>>>>>>>pinpoint before");
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) {
System.out.println(">>>>>>>>pinpoint after");
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(DemoPlugin.DEMO_SERVICE_TYPE);
recorder.recordApi(descriptor);
recorder.recordException(throwable);
recorder.recordAttribute(DemoPlugin.DEMO_ANNOTATION_KEY_INFO, args[0]);
} finally {
trace.traceBlockEnd();
}
}
private static boolean shouldTrace(Object[] args){
System.out.println(">>>>>>>>>>>>>>DemoInterceptor.shouldTrace>>>>>>>>>>>>>>>>>>>>>>");
return null!=args
&& args.length>0
&& (args[0] instanceof String)
&& ((String)args[0]).indexOf("pinpoint_bizlog_name")>-1;
}
}
上述的代码中,before和after方法分别代表AServiceImpl.buildCustomize方法执行前和执行后拦截器所做的事情,shouldTrace方法检查入参中是否有"pinpoint_xxx_name"前缀,如果没有就不执行拦截操作了(上述代码中注释掉了shouldTrace方法的判断),如果有,就执行trace操作,recorder.recordAttribute会将入参记录并在pinpoint追踪信息中展示出来;
plugins工程的配置
modeles
由于我们新建的demo工程和其他插件工程一样是plugins的子工程,为了能构建和打包,要在plugins工程中配置,打开plugins文件夹下的pom.xml文件:
首先,在modeles节点中增加以下内容:
<module>demo</module>
dependencies
然后,在dependencies节点增加以下内容:
<dependency>
<groupId>com.navercorp.pinpoint</groupId>
<artifactId>demo</artifactId>
<version>${project.version}</version>
</dependency>
构建
在demo项目下执行package命令
mvn install -Dmaven.test.skip=true
修改项目dockerfile,将demo对应的jar包添加到image中
FROM harbor.xxxx.com/taoche/library/java/jdk8:apm-1.7
ENV PROJECT_DIR=/opt/pinpoint-agent
WORKDIR $PROJECT_DIR
COPY docker/demo-1.8.2.jar $PROJECT_DIR/plugin/demo-1.7.3.jar
.......
演示
controller
@GetMapping
public String apm(@RequestParam(value = "type", defaultValue = "0") int type) {
IMGroup group = new IMGroup();
if (type == 0) {
new AServiceImpl().buildCustomize(group);
} else {
new BServiceImpl().buildCustomize(group);
}
return "SUCCESS";
}
在type为0时执行AServiceImpl,否则执行BServiceImpl
请求接口
分别添加type=1及type=0请求上述接口
apm日志
发现请求AServiceImpl时,记录了apm日志,原因是我们开发的插件对该类进行了拦截