上次讲了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、拦截器的实现
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();
}
}));
}
}
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 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/, 查看跟踪结果