Skip to content

Latest commit

 

History

History
175 lines (146 loc) · 6.06 KB

File metadata and controls

175 lines (146 loc) · 6.06 KB

Development-Guide

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.

Example for Apache HttpClient4.5

User want to get tracing information from Apache HttpClient.

Step 1

Add Apache HttpClient dependency in /zipkin/pom.xml

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.3</version>
</dependency>

Step 2

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)
        }
    }
}

Step 3

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

Step 4

Add HttpClientTracingInterceptor to com.megaease.easeagent.sniffer.Provider

    @Injection.Bean("supplier4HttpClient")
    public Supplier<AgentInterceptorChain.Builder> getSupplier4HttpClient() {
        return () -> ChainBuilderFactory.DEFAULT.createBuilder()
                .addInterceptor(new HttpClientTracingInterceptor());
    }

Step 5

Add HttpClientAdvice.class to com.megaease.easeagent.Easeagent

@Assembly({
        ... other advice class,
        HttpClientAdvice.class,

})

Step 6 For Junit Test

Add HttpClientAdvice.class to com.megaease.easeagent.sniffer.GeneratedTest like Step 4 Users can test whether the method interception works

Step 7 For Junit Test

Add HttpClientTracingInterceptorTest for test. Refer to FeignClientTracingInterceptorTest and other test code

Step 8

Build and run user application for test

mvn clean package -am -pl build