以前一篇文章中說到當咱們放棄spring-cloud-sleuth
這個組件時,會面臨兩個問題。首先是日誌中沒法顯示traceId和spanId這些鏈路信息,其次是不能在用spring-cloud-sleuth
所提供的方式進行鏈路傳值。如今就讓咱們來解決這兩個問題。上篇回顧java
spring-cloud-sleuth
是將traceId等鏈路信息保存在slf4j
的MDC(Mapped Diagnostic Contexts)中,而後經過%X{traceId}這種方式將traceId提取出來,好比打印到控制檯的默認格式是:git
%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(%5p [${spring.application.name:-},%X{X-B3-TraceId:-},%X{X-B3-SpanId:-},%X{X-Span-Export:-}]) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}
複製代碼
opentracing
中提供了ThreadLocalScopeManager這個類來管理調用上下文中的Span,咱們能夠繼承該類將traceId設置到MDC中。github
public class MDCScopeManager extends ThreadLocalScopeManager {
@Override
public Scope activate(Span span, boolean finishOnClose) {
return new ScopeWrapper(super.activate(span, finishOnClose));
}
@Override
public Scope activate(Span span) {
return new ScopeWrapper(super.activate(span));
}
private static class ScopeWrapper implements Scope {
private final Scope scope;
private final String previousTraceId;
private final String previousSpanId;
private final String previousParentSpanId;
private final String previousSampled;
ScopeWrapper(Scope scope) {
this.scope = scope;
this.previousTraceId = lookup("traceId");
this.previousSpanId = lookup("spanId");
this.previousParentSpanId = lookup("parentSpanId");
this.previousSampled = lookup("traceSampled");
JaegerSpanContext ctx = (JaegerSpanContext) scope.span().context();
String traceId = ctx.getTraceId();
String spanId = Long.toHexString(ctx.getSpanId());
String sampled = String.valueOf(ctx.isSampled());
String parentSpanId = Long.toHexString(ctx.getParentId());
replace("traceId", traceId);
replace("spanId", spanId);
replace("parentSpanId", parentSpanId);
replace("traceSampled", sampled);
}
@Override
public void close() {
this.scope.close();
replace("traceId", previousTraceId);
replace("spanId", previousSpanId);
replace("parentSpanId", previousParentSpanId);
replace("traceSampled", previousSampled);
}
@Override
public Span span() {
return this.scope.span();
}
}
private static String lookup(String key) {
return MDC.get(key);
}
private static void replace(String key, String value) {
if (value == null) {
MDC.remove(key);
} else {
MDC.put(key, value);
}
}
}
複製代碼
而後把這個類定義成Bean,這樣就能把它綁定到當前的tracer中去:spring
@Bean
public TracerBuilderCustomizer mdcBuilderCustomizer() {
return builder -> builder.withScopeManager(new MDCScopeManager());
}
複製代碼
而後利用%X{traceId}
這種方式設置打印格式,啓動程序後就能在控制檯中看到輸出了:bash
是否是和spring-cloud-sleuth
提供的方式同樣~~~多線程
opentracing
中提供了baggage元素來作跨進程的kv傳遞,咱們能夠利用baggage來傳遞咱們須要傳遞的值。(注意:同時他也會產生巨大的開銷,請當心使用此特性)app
public class TraceContext {
public static void setField(String key, String value) {
if (GlobalTracer.isRegistered() && StringUtils.isNotBlank(key) && StringUtils.isNotBlank(value)) {
Tracer tracer = GlobalTracer.get();
tracer.activeSpan().setBaggageItem(key, value);
}
}
public static String getFiled(String key, String defaultValue) {
if (GlobalTracer.isRegistered() && StringUtils.isNotBlank(key)) {
Tracer tracer = GlobalTracer.get();
return tracer.activeSpan().getBaggageItem(key);
}
return defaultValue;
}
}
複製代碼
項目中須要依賴opentracing-concurrent
:ide
<dependency>
<groupId>io.opentracing.contrib</groupId>
<artifactId>opentracing-concurrent</artifactId>
<version>0.4.0</version>
</dependency>
複製代碼
而後能夠經過TraceRunnable
來建立帶有trace的線程post
new Thread(new TracedRunnable(() -> {
//線程中幹活....
}, GlobalTracer.get()));
複製代碼
Spring環境中也能夠用@Autowired
來獲取tracerui
@Autowired
private Tracer tracer;
複製代碼