一、Javaagent
网上关于 Javaagent 的介绍很多,请找度娘和谷兄。唯一提的一点是字节码注入比较好用的是 bytebuddy,封装度很高,使用简单。
二、代码样例
以下为关键代码样例,可以依样画瓢自行改造。
1. 编写 agent 入口
package com.javashizhan.trace;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isSetter;
import static net.bytebuddy.matcher.ElementMatchers.nameContainsIgnoreCase;
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWithIgnoreCase;
import static net.bytebuddy.matcher.ElementMatchers.not;
import java.lang.instrument.Instrumentation;
import com.javashizhan.trace.interceptor.AbstractJunction;
import com.javashizhan.trace.interceptor.ProtectiveShieldMatcher;
import com.javashizhan.trace.interceptor.TraceInterceptor;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.NamedElement;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;
public class TraceAgent {public static void premain(String arguments, Instrumentation instrumentation) {new AgentBuilder.Default()
.type(buildMatch())
.transform((builder, type, classLoader, module) ->
builder.method(ElementMatchers.any())
.intercept(MethodDelegation.to(TraceInterceptor.class)) // 拦截器
).installOn(instrumentation);
}
public static ElementMatcher<? super TypeDescription> buildMatch() {ElementMatcher.Junction judge = new AbstractJunction<NamedElement>() {
@Override
public boolean matches(NamedElement target) {return true;}
};
judge = judge.and(not(isInterface())).and(not(isSetter()))
.and(nameStartsWithIgnoreCase("io.spring"))
.and(not(nameContainsIgnoreCase("util")))
.and(not(nameContainsIgnoreCase("interceptor")));
judge = judge.and(not(isSetter()));
return new ProtectiveShieldMatcher(judge);
}
}
2. 拦截器类 TraceInterceptor.java
package com.javashizhan.trace.interceptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import com.javashizhan.trace.domain.CallMethod;
import com.javashizhan.trace.TraceWrapper;
import com.javashizhan.trace.collector.DBCollector;
import com.javashizhan.trace.domain.Trace;
import com.javashizhan.trace.domain.TraceRecord;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;
public class TraceInterceptor {
@RuntimeType
public static Object intercept(@Origin Method method,
@SuperCall Callable<?> callable) throws Exception {before(method);
try {return callable.call();
} finally {after();
}
}
public static void after() {Trace trace = TraceWrapper.getTrace(); //Trace 类,可自行实现,不是关键
if (null != trace) {if (trace.callMethodSize() > 0) {CallMethod callMethod = trace.pop();
if (null != callMethod && callMethod.isTraceFlag()) {callMethod.calculateCostTime();
trace.addTraceRecord(new TraceRecord(callMethod));
}
if (trace.callMethodSize() == 0) {List<TraceRecord> traceRecordList = trace.getAllTraceRecord();
if (null != traceRecordList && traceRecordList.size() > 0) {DBCollector collector = new DBCollector(traceRecordList);
new Thread(collector).start();
TraceWrapper.destory();}
}
}
}
}
private static void before(Method method) {Trace trace = TraceWrapper.getTrace();
CallMethod callMethod = new CallMethod(method);
if (isInnerClass(callMethod)) { //spring 中有很多内部类,可以去掉
callMethod.setTraceFlag(false);
} else {callMethod.setTraceFlag(true);
}
// 不管是否跟踪都放进去
trace.push(callMethod);
}
private static boolean isInnerClass(CallMethod callMethod) {return callMethod.getClassName().indexOf('$') > -1;
}
}
3.AbstractJunction.java
package com.javashizhan.trace.interceptor;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatcher.Junction;
import net.bytebuddy.matcher.ElementMatcher.Junction.Conjunction;
import net.bytebuddy.matcher.ElementMatcher.Junction.Disjunction;
public abstract class AbstractJunction<V> implements ElementMatcher.Junction<V> {
@Override
public <U extends V> Junction<U> and(ElementMatcher<? super U> other) {return new Conjunction<U>(this, other);
}
@Override
public <U extends V> Junction<U> or(ElementMatcher<? super U> other) {return new Disjunction<U>(this, other);
}
}
4.ProtectiveShieldMatcher.java
package com.javashizhan.trace.interceptor;
import net.bytebuddy.matcher.ElementMatcher;
public class ProtectiveShieldMatcher<T> extends ElementMatcher.Junction.AbstractBase<T> {
private final ElementMatcher<? super T> matcher;
public ProtectiveShieldMatcher(ElementMatcher<? super T> matcher) {this.matcher = matcher;}
public boolean matches(T target) {
try {return this.matcher.matches(target);
} catch (Throwable t) {//logger.warn(t, "Byte-buddy occurs exception when match type.");
return false;
}
}
}
二、pom 文件
<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>
<groupId>trace</groupId>
<artifactId>chain</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<java.version>1.8</java.version>
<!-- <spring-cloud.version>Finchley.SR1</spring-cloud.version> -->
</properties>
<dependencies>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.9.6</version>
</dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy-agent</artifactId>
<version>1.9.6</version>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>2.7.9</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<!-- <Premain-Class>com.undergrowth.secure.SecurityAgent</Premain-Class> -->
<!-- <Premain-Class>com.undergrowth.agent.AgentToString</Premain-Class>-->
<Premain-Class>com.javashizhan.trace.TraceAgent</Premain-Class>
</manifestEntries>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
三、在 Java 应用中添加启动参数
1. 先将 agent 工程打成 jar 包
2. 在要使用 agent 的 Java 应用中添加如下 VM 启动参数
-javaagent:D:\MyApp\apache-skywalking-apm-bin\agent\chain-0.0.1-SNAPSHOT.jar
注意自行替换 jar 包路径。
end.
加入《Java 栈实战营》知识星球,参与讨论,更多实战代码分享,不就是几斤苹果,几个荣耀道具的事吗!
https://t.zsxq.com/RNzfi2j