The main function of EaseAgent is to collect Java method call trace and metrics information. You need to understand trace and metric before development.
EaseAgent use ByteBuddy to build agent core advice code.
After you understand the above information, you can follow the steps below to develop.
User want to get tracing information from Apache HttpClient.
Add Apache HttpClient dependency in /zipkin/pom.xml
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>
</dependency>Create XXXAdvice class in module sniffer
package com.megaease.easeagent.sniffer.httpclient.v4_5.advice;
import com.megaease.easeagent.common.ForwardLock;
import com.megaease.easeagent.core.AdviceTo;
import com.megaease.easeagent.core.Definition;
import com.megaease.easeagent.core.Injection;
import com.megaease.easeagent.core.Transformation;
import com.megaease.easeagent.core.interceptor.AgentInterceptorChain;
import com.megaease.easeagent.core.interceptor.AgentInterceptorChainInvoker;
import com.megaease.easeagent.sniffer.AbstractAdvice;
import com.megaease.easeagent.sniffer.Provider;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.matcher.ElementMatcher;
import java.util.Map;
import java.util.function.Supplier;
import static net.bytebuddy.matcher.ElementMatchers.hasSuperType;
import static net.bytebuddy.matcher.ElementMatchers.named;
@Injection.Provider(Provider.class)
public abstract class HttpClientAdvice implements Transformation {
@Override
public <T extends Definition> T define(Definition<T> def) {
return def.type(hasSuperType(named("org.apache.http.client.HttpClient"))) // enhanced client class
.transform(adviceExecute(named("execute"))) // "execute" is the name of HttpClient
.end();
}
/**
* This method is will be implements by sub class that generated by EaseAgent
*/
@AdviceTo(Execute.class)
protected abstract Definition.Transformer adviceExecute(ElementMatcher<? super MethodDescription> matcher);
public static class Execute extends AbstractAdvice {
/**
* @param supplier This argument will be injected by com.megaease.easeagent.sniffer.Provider.
* @param chainInvoker This argument will be injected by com.megaease.easeagent.sniffer.Provider
*/
@Injection.Autowire
public Execute(
@Injection.Qualifier("supplier4HttpClient") Supplier<AgentInterceptorChain.Builder> supplier,
AgentInterceptorChainInvoker chainInvoker) {
super(supplier, chainInvoker);
}
/**
* Refer to net.bytebuddy.asm.Advice
*/
@Advice.OnMethodEnter
ForwardLock.Release<Map<Object, Object>> enter(
@Advice.Origin Object invoker,
@Advice.Origin("#m") String method,
@Advice.AllArguments Object[] args
) {
return this.doEnter(invoker, method, args);
}
/**
* Refer to net.bytebuddy.asm.Advice
*/
@Advice.OnMethodExit(onThrowable = Throwable.class)
void exit(@Advice.Enter ForwardLock.Release<Map<Object, Object>> release,
@Advice.Origin Object invoker,
@Advice.Origin("#m") String method,
@Advice.AllArguments Object[] args,
// @Advice.Return Object retValue,
@Advice.Thrown Throwable throwable
) {
this.doExitNoRetValue(release, invoker, method, args, throwable);
// if user want return new result, user should use method:
// this.doExit(release, Advice, method, args, retValue, throwable)
}
}
}Create AgentInterceptor in module zipkin
package com.megaease.easeagent.zipkin.http.httpclient.v4_5;
import com.megaease.easeagent.core.interceptor.AgentInterceptor;
import com.megaease.easeagent.core.interceptor.AgentInterceptorChain;
import com.megaease.easeagent.core.interceptor.MethodInfo;
import java.util.Map;
/**
* About tracing and metric, User can refer to com.megaease.easeagent.zipkin.http.FeignClientTracingInterceptor
*/
public class HttpClientTracingInterceptor implements AgentInterceptor {
@Override
public void before(MethodInfo methodInfo, Map<Object, Object> context, AgentInterceptorChain chain) {
// process tracing or metric for method before
// finally, user can invoke next interceptor
chain.doBefore(methodInfo, context);
}
@Override
public Object after(MethodInfo methodInfo, Map<Object, Object> context, AgentInterceptorChain chain) {
// process tracing or metric for method after
// finally, user can invoke next interceptor
return AgentInterceptor.super.after(methodInfo, context, chain);
}
}Refer to FeignClientTracingInterceptor and other code
Add HttpClientTracingInterceptor to com.megaease.easeagent.sniffer.Provider
@Injection.Bean("supplier4HttpClient")
public Supplier<AgentInterceptorChain.Builder> getSupplier4HttpClient() {
return () -> ChainBuilderFactory.DEFAULT.createBuilder()
.addInterceptor(new HttpClientTracingInterceptor());
}Add HttpClientAdvice.class to com.megaease.easeagent.Easeagent
@Assembly({
... other advice class,
HttpClientAdvice.class,
})Add HttpClientAdvice.class to com.megaease.easeagent.sniffer.GeneratedTest like Step 4
Users can test whether the method interception works
Add HttpClientTracingInterceptorTest for test.
Refer to FeignClientTracingInterceptorTest and other test code
Build and run user application for test
mvn clean package -am -pl build