DefaultHttpClientInterceptor.java 4.6 KB
Newer Older
1
package org.skywalking.apm.plugin.feign.http.v9;
2

3 4
import feign.Request;
import feign.Response;
5 6
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
7 8 9 10 11 12 13
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
14 15 16 17 18 19 20 21 22 23 24
import org.skywalking.apm.agent.core.conf.Config;
import org.skywalking.apm.agent.core.context.ContextCarrier;
import org.skywalking.apm.agent.core.context.ContextManager;
import org.skywalking.apm.agent.core.plugin.interceptor.EnhancedClassInstanceContext;
import org.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodInvokeContext;
import org.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
import org.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
import org.skywalking.apm.trace.Span;
import org.skywalking.apm.trace.tag.Tags;

/**
25
 * {@link DefaultHttpClientInterceptor} intercept the default implementation of http calls by the Feign.
26 27 28
 *
 * @author pengys5
 */
29
public class DefaultHttpClientInterceptor implements InstanceMethodsAroundInterceptor {
30

31
    private static final String COMPONENT_NAME = "FeignDefaultHttp";
32 33

    /**
34 35 36
     * Get the {@link feign.Request} from {@link EnhancedClassInstanceContext}, then create {@link Span} and set host,
     * port, kind, component, url from {@link feign.Request}.
     * Through the reflection of the way, set the http header of context data into {@link feign.Request#headers}.
37 38 39 40 41 42 43 44 45
     *
     * @param context instance context, a class instance only has one {@link EnhancedClassInstanceContext} instance.
     * @param interceptorContext method context, includes class name, method name, etc.
     * @param result change this result, if you want to truncate the method.
     * @throws Throwable
     */
    @Override
    public void beforeMethod(EnhancedClassInstanceContext context, InstanceMethodInvokeContext interceptorContext,
        MethodInterceptResult result) throws Throwable {
46
        Request request = (Request)interceptorContext.allArguments()[0];
47

48 49 50 51
        URL url = new URL(request.url());
        Span span = ContextManager.createSpan(request.url());
        Tags.PEER_PORT.set(span, url.getPort());
        Tags.PEER_HOST.set(span, url.getHost());
52 53 54
        Tags.SPAN_KIND.set(span, Tags.SPAN_KIND_CLIENT);
        Tags.COMPONENT.set(span, COMPONENT_NAME);
        Tags.HTTP.METHOD.set(span, request.method());
55
        Tags.URL.set(span, url.getPath());
56 57 58 59 60
        Tags.SPAN_LAYER.asHttp(span);

        ContextCarrier contextCarrier = new ContextCarrier();
        ContextManager.inject(contextCarrier);

61 62 63
        List<String> contextCollection = new ArrayList<String>();
        contextCollection.add(contextCarrier.serialize());

64 65 66 67 68 69
        Field headersField = Request.class.getDeclaredField("headers");
        Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        modifiersField.setInt(headersField, headersField.getModifiers() & ~Modifier.FINAL);

        headersField.setAccessible(true);
70 71 72 73 74
        Map<String, Collection<String>> headers = new LinkedHashMap<String, Collection<String>>();
        headers.put(Config.Plugin.Http.HEADER_NAME_OF_CONTEXT_DATA, contextCollection);
        headers.putAll(request.headers());

        headersField.set(request, Collections.unmodifiableMap(headers));
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
    }

    /**
     * Get the status code from {@link Response}, when status code greater than 400, it means there was some errors in
     * the server.
     * Finish the {@link Span}.
     *
     * @param context instance context, a class instance only has one {@link EnhancedClassInstanceContext} instance.
     * @param interceptorContext method context, includes class name, method name, etc.
     * @param ret the method's original return value.
     * @return
     * @throws Throwable
     */
    @Override
    public Object afterMethod(EnhancedClassInstanceContext context, InstanceMethodInvokeContext interceptorContext,
        Object ret) throws Throwable {
        Response response = (Response)ret;
92
        int statusCode = response.status();
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110

        Span span = ContextManager.activeSpan();
        if (statusCode >= 400) {
            Tags.ERROR.set(span, true);
        }

        Tags.STATUS_CODE.set(span, statusCode);
        ContextManager.stopSpan();

        return ret;
    }

    @Override public void handleMethodException(Throwable t, EnhancedClassInstanceContext context,
        InstanceMethodInvokeContext interceptorContext) {
        Tags.ERROR.set(ContextManager.activeSpan(), true);
        ContextManager.activeSpan().log(t);
    }
}