解决代码使用CompletableFuture做异步时spring-cloud-starter-sleuth的日志追踪号为空的情况
产生问题原因
就是异步调用,导致spanId和traceId丢失了
@Async 注解的异步调用是没问题的
前提
使用spring-cloud-starter-sleuth jar包版本2.2.8.RELEASE
关于追踪号的xml 配置为
<pattern>%yellow(%date{yyyy-MM-dd HH:mm:ss.SSS}) [%X{X-B3-TraceId:-},%X{X-B3-SpanId:-},%X{X-B3-ParentSpanId:-},%X{X-Span-Export:-}] %highlight(%-5level) %yellow(%thread) %green(%logger:%line) %msg%n</pattern>
<pattern>%yellow(%date{yyyy-MM-dd HH:mm:ss.SSS}) [%X{traceId:-},%X{spanId:-},%X{parentId:-},%X{spanExportable:false}] %highlight(%-5level) %yellow(%thread) %green(%logger:%line) %msg%n</pattern>
使用自定义线程池+MDC解决
线程池也可以用这中方法解决
1、自定义线程池
import org.slf4j.MDC;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class CustomMdcThreadPoolWrapper extends ThreadPoolExecutor {
public CustomMdcThreadPoolWrapper(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
public CustomMdcThreadPoolWrapper(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
}
public CustomMdcThreadPoolWrapper(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
}
public CustomMdcThreadPoolWrapper(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
}
@Override
public void execute(Runnable task) {
super.execute(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()));
}
@Override
public <T> Future<T> submit(Runnable task, T result) {
return super.submit(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()), result);
}
@Override
public <T> Future<T> submit(Callable<T> task) {
return super.submit(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()));
}
@Override
public Future<?> submit(Runnable task) {
return super.submit(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()));
}
import org.slf4j.MDC;
import java.util.Map;
import java.util.concurrent.Callable;
public class ThreadMdcUtil {
public static <T> Callable<T> wrap(final Callable<T> callable, final Map<String, String> context) {
return () -> {
if (context == null) {
MDC.clear();
} else {
MDC.setContextMap(context);
}
try {
return callable.call();
} finally {
MDC.clear();
}
};
}
public static Runnable wrap(final Runnable runnable, final Map<String, String> context) {
return () -> {
if (context == null) {
MDC.clear();
} else {
MDC.setContextMap(context);
}
try {
runnable.run();
} finally {
MDC.clear();
}
};
}
}
2、使用
public String testMdc() {
log.info("测试自定义线程池");
CompletableFuture.runAsync(() ->
print("zhangsan")
, customMdcThreadPoolWrapper);
CompletableFuture.runAsync(() ->
print("lisi")
, customMdcThreadPoolWrapper);
return "testMdc ok";
}
当CompletableFuture有一个任务时,代码的写法
import brave.Span;
import brave.Tracer;
@Autowired
private Tracer tracer;
public String completableFutureTestOne() {
log.info("--------");
Span span = tracer.nextSpan();
CompletableFuture.supplyAsync(()->{
try (Tracer.SpanInScope cleared = tracer.withSpanInScope(span)) {
print("zhangsan");
return "zhangsan";
}catch (Exception e) {
e.printStackTrace();
return "zhangsan";
}
});
return "ok111";
}
当CompletableFuture有多个任务时,代码的写法
import brave.Span;
import brave.Tracer;
import brave.propagation.TraceContext;
@Autowired
private Tracer tracer;
public String completableFutureTest() {
log.info("--------");
TraceContext context = tracer.currentSpan().context();
// 也可以按照下一行获取TraceContext
// TraceContext context = tracer.nextSpan().context();
CompletableFuture.supplyAsync(()->{
Span span = tracer.newChild(context);
try (Tracer.SpanInScope cleared = tracer.withSpanInScope(span)) {
print("zhangsan");
return "zhangsan";
}catch (Exception e) {
e.printStackTrace();
return "zhangsan";
}
});
CompletableFuture.supplyAsync(()->{
Span span = tracer.newChild(context);
try (Tracer.SpanInScope cleared = tracer.withSpanInScope(span)) {
print("lisi");
return "lisi";
}catch (Exception e) {
e.printStackTrace();
return "lisi";
}
});
return "ok";
}
纸上得来终觉浅,绝知此事要躬行。