天天看點

使用Zipkin 和 Brave 實作http(springmvc)服務調用跟蹤(二)使用Zipkin 和 Brave 實作http(springmvc)服務調用跟蹤(二)

上次講了Brave為Spring提供的Servlet攔截器(ServletHandlerInterceptor)及Rest(BraveClientHttpRequestInterceptor)模闆的攔截器,向zipkin報告監控資料,其中,BraveClientHttpRequestInterceptor負責cs與cr的處理,ServletHandlerInterceptor負責sr與ss的處理,并沒有記錄到請求參數的監控及使用的是預設的span名稱,那如何才能記錄請求參數及修改span名稱。

需要實作自定義攔截器,我選擇在服務端做處理記錄請求參數的過程,先看看Brave提供服務端攔截器ServletHandlerInterceptor的源碼實作,發現使用HttpServerRequestAdapter擴充卡,

使用Zipkin 和 Brave 實作http(springmvc)服務調用跟蹤(二)使用Zipkin 和 Brave 實作http(springmvc)服務調用跟蹤(二)

再跟蹤HttpServerRequestAdapter源碼,在其中實作ServerRequestAdapter接口,包括跟蹤資料的擷取,span名稱的擷取及鍵值資料的添加。

使用Zipkin 和 Brave 實作http(springmvc)服務調用跟蹤(二)使用Zipkin 和 Brave 實作http(springmvc)服務調用跟蹤(二)

那我們除了實作自定義攔截器,還需要實作自己的擴充卡,擴充卡實作接口ServerRequestAdapter,在擷取鍵值資料的方法中實作添加請求參數到keyvalue,在span名稱擷取方法傳回自己span名稱,即完成了請求參數的添加及span名稱的自定義

以下實作在上篇中提供的git的工程中實作,可直接下載下傳,git位址:https://github.com/blacklau/brave-webmvc-example

1、攔截器的實作

<b>[java]</b> view plain copy

package brave.interceptor;

import static com.github.kristofa.brave.internal.Util.checkNotNull;

import java.net.URI;

import java.net.URISyntaxException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.annotation.Configuration;

import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.github.kristofa.brave.Brave;

import com.github.kristofa.brave.ServerRequestInterceptor;

import com.github.kristofa.brave.ServerResponseInterceptor;

import com.github.kristofa.brave.ServerSpan;

import com.github.kristofa.brave.ServerSpanThreadBinder;

import com.github.kristofa.brave.http.DefaultSpanNameProvider;

import com.github.kristofa.brave.http.HttpResponse;

import com.github.kristofa.brave.http.HttpServerRequest;

import com.github.kristofa.brave.http.HttpServerRequestAdapter;

import com.github.kristofa.brave.http.HttpServerResponseAdapter;

import com.github.kristofa.brave.http.SpanNameProvider;

import com.github.kristofa.brave.spring.ServletHandlerInterceptor;

import brave.adapter.CustomServerRequestAdapter;

@Configuration

public class CustomServletHandlerInterceptor  extends HandlerInterceptorAdapter {

    static final String HTTP_SERVER_SPAN_ATTRIBUTE = ServletHandlerInterceptor.class.getName() + ".customserver-span";

    /** Creates a tracing interceptor with custom */

    public static CustomServletHandlerInterceptor create(Brave brave) {

        return new Builder(brave).build();

    }

    public static Builder builder(Brave brave) {

        return new Builder(brave);

    public static final class Builder {

        final Brave brave;

        SpanNameProvider spanNameProvider = new DefaultSpanNameProvider();

        Builder(Brave brave) { // intentionally hidden

            this.brave = checkNotNull(brave, "brave");

        }

        public Builder spanNameProvider(SpanNameProvider spanNameProvider) {

            this.spanNameProvider = checkNotNull(spanNameProvider, "spanNameProvider");

            return this;

        public CustomServletHandlerInterceptor build() {

            return new CustomServletHandlerInterceptor(this);

    private final ServerRequestInterceptor requestInterceptor;

    private final ServerResponseInterceptor responseInterceptor;

    private final ServerSpanThreadBinder serverThreadBinder;

    private final SpanNameProvider spanNameProvider;

    @Autowired // internal

    CustomServletHandlerInterceptor(SpanNameProvider spanNameProvider, Brave brave) {

        this(builder(brave).spanNameProvider(spanNameProvider));

    CustomServletHandlerInterceptor(Builder b) { // intentionally hidden

        this.requestInterceptor = b.brave.serverRequestInterceptor();

        this.responseInterceptor = b.brave.serverResponseInterceptor();

        this.serverThreadBinder = b.brave.serverSpanThreadBinder();

        this.spanNameProvider = b.spanNameProvider;

    /**

     * @deprecated please use {@link #create(Brave)} or {@link #builder(Brave)}

     */

    @Deprecated

    public CustomServletHandlerInterceptor(ServerRequestInterceptor requestInterceptor, ServerResponseInterceptor responseInterceptor, SpanNameProvider spanNameProvider, final ServerSpanThreadBinder serverThreadBinder) {

        this.requestInterceptor = requestInterceptor;

        this.spanNameProvider = spanNameProvider;

        this.responseInterceptor = responseInterceptor;

        this.serverThreadBinder = serverThreadBinder;

    @Override

    public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) {

        requestInterceptor.handle(new CustomServerRequestAdapter(request, spanNameProvider));

        return true;

    public void afterConcurrentHandlingStarted(final HttpServletRequest request, final HttpServletResponse response, final Object handler) {

        request.setAttribute(HTTP_SERVER_SPAN_ATTRIBUTE, serverThreadBinder.getCurrentServerSpan());

        serverThreadBinder.setCurrentSpan(null);

    public void afterCompletion(final HttpServletRequest request, final HttpServletResponse response, final Object handler, final Exception ex) {

        final ServerSpan span = (ServerSpan) request.getAttribute(HTTP_SERVER_SPAN_ATTRIBUTE);

        if (span != null) {

            serverThreadBinder.setCurrentSpan(span);

       responseInterceptor.handle(new HttpServerResponseAdapter(new HttpResponse() {

           @Override

           public int getHttpStatusCode() {

               return response.getStatus();

           }

       }));

}

2、擴充卡的實作

package brave.adapter;

import static com.github.kristofa.brave.IdConversion.convertToLong;

import java.util.ArrayList;

import java.util.Collection;

import java.util.Collections;

import java.util.List;

import java.util.Map;

import com.github.kristofa.brave.KeyValueAnnotation;

import com.github.kristofa.brave.ServerRequestAdapter;

import com.github.kristofa.brave.SpanId;

import com.github.kristofa.brave.TraceData;

import com.github.kristofa.brave.http.BraveHttpHeaders;

import zipkin.TraceKeys;

public class CustomServerRequestAdapter  implements ServerRequestAdapter {

    private final HttpServletRequest request;

    public CustomServerRequestAdapter(HttpServletRequest request, SpanNameProvider spanNameProvider) {

        this.request = request;

    public TraceData getTraceData() {

        String sampled = request.getHeader(BraveHttpHeaders.Sampled.getName());

        String parentSpanId = request.getHeader(BraveHttpHeaders.ParentSpanId.getName());

        String traceId = request.getHeader(BraveHttpHeaders.TraceId.getName());

        String spanId = request.getHeader(BraveHttpHeaders.SpanId.getName());

        // Official sampled value is 1, though some old instrumentation send true

        Boolean parsedSampled = sampled != null

            ? sampled.equals("1") || sampled.equalsIgnoreCase("true")

            : null;

        if (traceId != null &amp;&amp; spanId != null) {

            return TraceData.create(getSpanId(traceId, spanId, parentSpanId, parsedSampled));

        } else if (parsedSampled == null) {

            return TraceData.EMPTY;

        } else if (parsedSampled.booleanValue()) {

            // Invalid: The caller requests the trace to be sampled, but didn't pass IDs

        } else {

            return TraceData.NOT_SAMPLED;

    public String getSpanName() {

        return "custom spanName";

    public Collection&lt;KeyValueAnnotation&gt; requestAnnotations() {

        List&lt;KeyValueAnnotation&gt; kvs = new ArrayList&lt;KeyValueAnnotation&gt;();

        Map&lt;String, String[]&gt; params = this.request.getParameterMap();

        for(String key:params.keySet()){

            KeyValueAnnotation kv = KeyValueAnnotation.create(key, params.get(key)[0]);

            kvs.add(kv);

        KeyValueAnnotation uriAnnotation = KeyValueAnnotation.create(

                TraceKeys.HTTP_URL, request.getRequestURI().toString());

        kvs.add(uriAnnotation);

        return kvs;

    static SpanId getSpanId(String traceId, String spanId, String parentSpanId, Boolean sampled) {

        return SpanId.builder()

            .traceIdHigh(traceId.length() == 32 ? convertToLong(traceId, 0) : 0)

            .traceId(convertToLong(traceId))

            .spanId(convertToLong(spanId))

            .sampled(sampled)

            .parentId(parentSpanId == null ? null : convertToLong(parentSpanId)).build();

   }

浏覽器輸入測試: http://localhost:80810brave-webmvc-example/a?service=account.user.login

打開zipkin:  http://localhost:9411/, 檢視跟蹤結果

使用Zipkin 和 Brave 實作http(springmvc)服務調用跟蹤(二)使用Zipkin 和 Brave 實作http(springmvc)服務調用跟蹤(二)