APMAgentV1.java
package com.liq.app2;
import com.liq.javaagent.collector.*;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.LoaderClassPath;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class APMAgentV1 implements ClassFileTransformer {
private static Collector[] collectors;
static {
collectors = new Collector[]{OtherCollector.INSTANCE};
}
private Map classPoolMap = new ConcurrentHashMap<>();
@Override
public byte[] transform(ClassLoader classLoader, String className, Class> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
if ((className == null) || (classLoader == null) || (classLoader.getClass().getName().equals("sun.reflect.DelegatingClassLoader")) || (classLoader.getClass().getName().equals("org.apache.catalina.loader.StandardClassLoader")) || (classLoader.getClass().getName().equals("javax.management.remote.rmi.NoCallStackClassLoader")) || (classLoader.getClass().getName().equals("com.alibaba.fastjson.util.ASMClassLoader")) || (className.indexOf("$Proxy") != -1) || (className.startsWith("java"))) {
return null;
}
// 不同的ClassLoader使用不同的ClassPool
ClassPool localClassPool;
if (!this.classPoolMap.containsKey(classLoader)) {
localClassPool = new ClassPool();
localClassPool.insertClassPath(new LoaderClassPath(classLoader));
this.classPoolMap.put(classLoader, localClassPool);
} else {
localClassPool = this.classPoolMap.get(classLoader);
}
try {
className = className.replaceAll("/", ".");
CtClass localCtClass = localClassPool.get(className);
for (Collector collector : collectors) {
if (collector.isTarget(className, classLoader, localCtClass)) {
byte[] arrayOfByte = collector.transform(classLoader, className, classfileBuffer, localCtClass);
System.out.println(String.format("%s APM agent insert success", new Object[]{className}));
return arrayOfByte;
}
}
} catch (Throwable localThrowable) {
new Exception(String.format("%s APM agent insert fail", new Object[]{className}), localThrowable).printStackTrace();
}
return new byte[0];
}
public static void premain(String agentArgs, Instrumentation inst) {
System.out.println("Hello, world! JavaAgen");
System.out.println("agentArgs: " + agentArgs);
inst.addTransformer(new APMAgentV1());
}
}
ClassWrapper.java
用于向目标类插入一些监控代码
package com.liq.app2;
import javassist.CtMethod;
import javassist.NotFoundException;
public class ClassWrapper {
private String beginSrc;
private String endSrc;
private String errorSrc;
public ClassWrapper beginSrc(String paramString) {
this.beginSrc = paramString;
return this;
}
public ClassWrapper endSrc(String paramString) {
this.endSrc = paramString;
return this;
}
public ClassWrapper errorSrc(String paramString) {
this.errorSrc = paramString;
return this;
}
public String beginSrc(CtMethod ctMethod) {
try {
String template = ctMethod.getReturnType().getName().equals("void")
?
"{\n" +
" %s \n" +
" try {\n" +
" %s$agent($$);\n" +
" } catch (Throwable e) {\n" +
" %s\n" +
" throw e;\n" +
" }finally{\n" +
" %s\n" +
" }\n" +
"}"
:
"{\n" +
" %s \n" +
" Object result=null;\n" +
" try {\n" +
" result=($w)%s$agent($$);\n" +
" } catch (Throwable e) {\n" +
" %s \n" +
" throw e;\n" +
" }finally{\n" +
" %s \n" +
" }\n" +
" return ($r) result;\n" +
"}";
String insertBeginSrc = this.beginSrc == null ? "" : this.beginSrc;
String insertErrorSrc = this.errorSrc == null ? "" : this.errorSrc;
String insertEndSrc = this.endSrc == null ? "" : this.endSrc;
String result = String.format(template, new Object[]{insertBeginSrc, ctMethod.getName(), insertErrorSrc, insertEndSrc});
return result;
} catch (NotFoundException localNotFoundException) {
throw new RuntimeException(localNotFoundException);
}
}
}
ClassReplacer.java
用于使用包装过的类替换目标类
package com.liq.app2;
import javassist.CannotCompileException;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;
import java.io.IOException;
public class ClassReplacer
{
private final String className;
private final ClassLoader classLoader;
private final CtClass ctClass;
public ClassReplacer(String className, ClassLoader classLoader, CtClass ctClass)
{
this.className = className;
this.classLoader = classLoader;
this.ctClass = ctClass;
}
public void replace(CtMethod ctMethod, ClassWrapper paramd) throws CannotCompileException {
String methodName = ctMethod.getName();
CtMethod localCtMethod2 = CtNewMethod.copy(ctMethod, methodName, this.ctClass, null);
localCtMethod2.setName(methodName + "$agent");
this.ctClass.addMethod(localCtMethod2);
ctMethod.setBody(paramd.beginSrc(ctMethod));
}
public byte[] replace() throws IOException, CannotCompileException {
return this.ctClass.toBytecode();
}
}
Collector.java
监控信息搜集器接口
package com.liq.app2.collector;
import javassist.CtClass;
public interface Collector {
boolean isTarget(String className, ClassLoader classLoader, CtClass ctClass);
byte[] transform(ClassLoader classLoader, String className, byte[] classfileBuffer, CtClass ctClass);
}
OtherCollector.java
监控信息搜集器实现,这里只是简单例子,所以写死了目标类名字“com.liq.service.UserService”
package com.liq.app2.collector;
import com.liq.app2.ClassReplacer;
import com.liq.app2.ClassWrapper;
import com.liq.app2.stat.Statistics;
import javassist.*;
public class OtherCollector implements Collector {
public static final OtherCollector INSTANCE = new OtherCollector();
private OtherCollector() {
}
private static final String beginSrc;
private static final String endSrc = "inst.end(statistic);";
private static final String errorSrc;
static {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("com.liq.app2.collector.OtherCollector inst=com.liq.app2.collector.OtherCollector.INSTANCE;");
stringBuilder.append("com.liq.app2.stat.Statistics statistic = inst.start(\"%s\");");
beginSrc = stringBuilder.toString();
errorSrc = "inst.error(statistic,e);";
}
@Override
public boolean isTarget(String className, ClassLoader classLoader, CtClass ctClass) {
return "com.liq.service.UserService".equals(className);
}
@Override
public byte[] transform(ClassLoader classLoader, String className, byte[] classfileBuffer, CtClass ctClass) {
try {
ClassReplacer replacer = new ClassReplacer(className, classLoader, ctClass);
for (CtMethod ctMethod : ctClass.getDeclaredMethods()) {
String str;
if ((Modifier.isPublic(ctMethod.getModifiers())) && (!Modifier.isStatic(ctMethod.getModifiers()) && (!Modifier.isNative(ctMethod.getModifiers())))) {
ClassWrapper classWrapper = new ClassWrapper();
classWrapper.beginSrc(String.format(beginSrc, ctMethod.getLongName()));
classWrapper.endSrc(endSrc);
classWrapper.errorSrc(errorSrc);
replacer.replace(ctMethod, classWrapper);
}
}
return replacer.replace();
} catch (Exception e) {
e.printStackTrace();
}
return new byte[0];
}
public Statistics start(String methodSign) {
return new Statistics(methodSign);
}
public void end(Statistics statistics) {
statistics.end();
}
public void error(Statistics statistics, Throwable e) {
statistics.error(e);
}
}
Statistics
package com.liq.app2.stat;
public class Statistics {
private String method;
private long startTime;
private long endTime;
private Throwable error;
public Statistics(String method) {
this.method = method;
this.startTime = System.currentTimeMillis();
}
public void end() {
endTime = System.currentTimeMillis();
System.out.println(this.toString());
}
public void error(Throwable e) {
error = e;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Statistics{");
sb.append("method='").append(method).append('\'');
sb.append(", startTime=").append(startTime);
sb.append(", endTime=").append(endTime);
sb.append(", error=").append(error);
sb.append('}');
return sb.toString();
}
}
maven依赖
javassist
javassist
3.12.1.GA