网上关于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; } } }
<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>
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