使用Zipkin 和 Brave 實現http(springmvc)服務呼叫跟蹤(二)
上次講了Brave為Spring提供的Servlet攔截器(ServletHandlerInterceptor)及Rest(BraveClientHttpRequestInterceptor)模板的攔截器,向zipkin報告監控資料,其中,BraveClientHttpRequestInterceptor負責cs與cr的處理,ServletHandlerInterceptor負責sr與ss的處理,並沒有記錄到請求引數的監控及使用的是預設的span名稱,那如何才能記錄請求引數及修改span名稱。
需要實現自定義攔截器,我選擇在服務端做處理記錄請求引數的過程,先看看Brave提供服務端攔截器ServletHandlerInterceptor的原始碼實現,發現使用HttpServerRequestAdapter介面卡,
再跟蹤HttpServerRequestAdapter原始碼,在其中實現ServerRequestAdapter介面,包括跟蹤資料的獲取,span名稱的獲取及鍵值資料的新增。
那我們除了實現自定義攔截器,還需要實現自己的介面卡,介面卡實現介面ServerRequestAdapter,在獲取鍵值資料的方法中實現新增請求引數到keyvalue,在span名稱獲取方法返回自己span名稱,即完成了請求引數的新增及span名稱的自定義
以下實現在上篇中提供的git的工程中實現,可直接下載,git地址:https://github.com/blacklau/brave-webmvc-example
1、攔截器的實現
2、介面卡的實現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; } @Override public void afterConcurrentHandlingStarted(final HttpServletRequest request, final HttpServletResponse response, final Object handler) { request.setAttribute(HTTP_SERVER_SPAN_ATTRIBUTE, serverThreadBinder.getCurrentServerSpan()); serverThreadBinder.setCurrentSpan(null); } @Override 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(); } })); } }
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 javax.servlet.http.HttpServletRequest;
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 com.github.kristofa.brave.http.SpanNameProvider;
import zipkin.TraceKeys;
public class CustomServerRequestAdapter implements ServerRequestAdapter {
private final HttpServletRequest request;
public CustomServerRequestAdapter(HttpServletRequest request, SpanNameProvider spanNameProvider) {
this.request = request;
}
@Override
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 && 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
return TraceData.EMPTY;
} else {
return TraceData.NOT_SAMPLED;
}
}
@Override
public String getSpanName() {
return "custom spanName";
}
@Override
public Collection<KeyValueAnnotation> requestAnnotations() {
List<KeyValueAnnotation> kvs = new ArrayList<KeyValueAnnotation>();
Map<String, String[]> 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/, 檢視跟蹤結果