我使用的是MDC Logger,除了一种情况外,它对我来说非常有效。无论代码在何处使用了CompletableFuture,对于已创建的线程,MDC数据都不会传递到下一个线程,并且由于日志失败而导致该错误。例如,在以下代码段中我用于创建新线程的代码中。
@Autowired
private CloudBlobClient cloudBlobClient;
@Autowired
private MSITokenGenerator msiTokenGenerator;
@Bean(name = {"storageClient"})
public StorageClient getStorageClient() {
LOG.debug("I am inside storage class");
StorageClient ac = null;
try {
ac = new StorageClient();
ac.setCloudBlobClient(cloudBlobClient);
ac.setMsiTokenGenerator(msiTokenGenerator);
return ac;
}
}
以及以下日志的结果
CompletableFuture.runAsync(() -> getAcountDetails(user));
下面是我的MDC数据,该数据未通过线程2019-04-29 11:44:13,690 INFO | /app/rest/controller/userdetails | f80fdc1f-8123-3932-a405-dda2dc2a80d5 |[http-nio-8182-exec-5] RestServiceExecutor: service:
2019-04-29 11:44:13,690 INFO | /app/rest/controller/userdetails | f80fdc1f-8123-3932-a405-dda2dc2a80d5 |[http-nio-8182-exec-5] RestServiceExecutor:
2019-04-29 11:44:13,779 INFO | /app/rest/controller/userdetails | f80fdc1f-8123-3932-a405-dda2dc2a80d5 |[http-nio-8182-exec-5] UserDetailsRepoImpl:
2019-04-29 11:44:13,950 INFO [ForkJoinPool.commonPool-worker-3] RestServiceExecutor: header:
2019-04-29 11:44:13,950 INFO [ForkJoinPool.commonPool-worker-3] RestServiceExecutor: service:
2019-04-29 11:44:14,012 INFO [ForkJoinPool.commonPool-worker-3] CommonMasterDataServiceImpl: Cache: Retrieving Config Data details.
2019-04-29 11:44:14,028 INFO [ForkJoinPool.commonPool-worker-3] CommonMasterDataServiceImpl: Cache: Retrieved Config Data details : 1
2019-04-29 11:44:14,028 INFO [ForkJoinPool.commonPool-worker-3] CommonMasterDataServiceImpl: Cache: Retrieving Config Data details.
2019-04-29 11:44:14,033 INFO [ForkJoinPool.commonPool-worker-3] CommonMasterDataServiceImpl: Cache: Retrieved Config Data details : 1
2019-04-29 11:44:14,147 INFO | /app/rest/controller/userdetails | f80fdc1f-8123-3932-a405-dda2dc2a80d5 |[http-nio-8182-exec-5] SecondaryCacheServiceImpl: Fetching from secondary cache
2019-04-29 11:44:14,715 INFO | /app/rest/controller/userdetails | f80fdc1f-8123-3932-a405-dda2dc2a80d5 |[http-nio-8182-exec-5] CommonMasterDataServiceImpl: Cache: Retrieving Config Data details.
2019-04-29 11:44:14,749 INFO | /app/rest/controller/userdetails | f80fdc1f-8123-3932-a405-dda2dc2a80d5 |[http-nio-8182-exec-5]
传递
[ForkJoinPool.commonPool-worker-3]
下面是我的logback.xml配置,其中 sessionID 是MDC数据
| /app/rest/controller/userdetails | f80fdc1f-8123-3932-a405-dda2dc2a80d5 |
我尝试了以下链接
最适合TaskExecutor。但是我没有找到CompletableFuture的任何解决方案。
答案 0 :(得分:2)
创建包装器方法
static CompletableFuture<Void> myMethod(Runnable runnable) {
Map<String, String> previous = MDC.getCopyOfContextMap();
return CompletableFuture.runAsync(() -> {
MDC.setContextMap(previous);
try {
runnable.run();
} finally {
MDC.clear();
}
});
}
并使用它代替CompletableFuture.runAsync
。
答案 1 :(得分:1)
我的解决方案主题是(它将与JDK 9+一起使用,因为从该版本开始就公开了几个可重写的方法)
让完整的生态系统了解MDC
为此,我们需要解决以下情况:
为此,我们通过扩展它来创建CompletableFuture
的MDC感知版本类。我的版本如下所示
import org.slf4j.MDC;
import java.util.Map;
import java.util.concurrent.*;
import java.util.function.Function;
import java.util.function.Supplier;
public class MDCAwareCompletableFuture<T> extends CompletableFuture<T> {
public static final ExecutorService MDC_AWARE_ASYNC_POOL = new MDCAwareForkJoinPool();
@Override
public CompletableFuture newIncompleteFuture() {
return new MDCAwareCompletableFuture();
}
@Override
public Executor defaultExecutor() {
return MDC_AWARE_ASYNC_POOL;
}
public static <T> CompletionStage<T> getMDCAwareCompletionStage(CompletableFuture<T> future) {
return new MDCAwareCompletableFuture<>()
.completeAsync(() -> null)
.thenCombineAsync(future, (aVoid, value) -> value);
}
public static <T> CompletionStage<T> getMDCHandledCompletionStage(CompletableFuture<T> future,
Function<Throwable, T> throwableFunction) {
Map<String, String> contextMap = MDC.getCopyOfContextMap();
return getMDCAwareCompletionStage(future)
.handle((value, throwable) -> {
setMDCContext(contextMap);
if (throwable != null) {
return throwableFunction.apply(throwable);
}
return value;
});
}
}
MDCAwareForkJoinPool
类看起来像
public class MDCAwareForkJoinPool extends ForkJoinPool {
//Override constructors which you need
@Override
public <T> ForkJoinTask<T> submit(Callable<T> task) {
return super.submit(MDCUtility.wrapWithMdcContext(task));
}
@Override
public <T> ForkJoinTask<T> submit(Runnable task, T result) {
return super.submit(wrapWithMdcContext(task), result);
}
@Override
public ForkJoinTask<?> submit(Runnable task) {
return super.submit(wrapWithMdcContext(task));
}
@Override
public void execute(Runnable task) {
super.execute(wrapWithMdcContext(task));
}
}
要包装的实用方法例如
public static <T> Callable<T> wrapWithMdcContext(Callable<T> task) {
//save the current MDC context
Map<String, String> contextMap = MDC.getCopyOfContextMap();
return () -> {
setMDCContext(contextMap);
try {
return task.call();
} finally {
// once the task is complete, clear MDC
MDC.clear();
}
};
}
public static Runnable wrapWithMdcContext(Runnable task) {
//save the current MDC context
Map<String, String> contextMap = MDC.getCopyOfContextMap();
return () -> {
setMDCContext(contextMap);
try {
return task.run();
} finally {
// once the task is complete, clear MDC
MDC.clear();
}
};
}
public static void setMDCContext(Map<String, String> contextMap) {
MDC.clear();
if (contextMap != null) {
MDC.setContextMap(contextMap);
}
}
以下是一些使用准则:
MDCAwareCompletableFuture
而不是类CompletableFuture
。CompletableFuture
中的几个方法实例化自身版本,例如new CompletableFuture...
。对于此类方法(大多数公共静态方法),请使用替代方法来获取MDCAwareCompletableFuture
的实例。使用替代方法的一个例子可能是使用CompletableFuture.supplyAsync(...)
new MDCAwareCompletableFuture<>().completeAsync(...)
CompletableFuture
实例而陷入困境时,使用方法MDCAwareCompletableFuture
将getMDCAwareCompletionStage
的实例转换为CompletableFuture
到MDCAwareForkJoinPool
。显然,您无法在该库中保留上下文,但是在您的代码触及应用程序代码后,此方法仍将保留上下文。MDCAwareThreadPoolExecutor
。您也可以通过覆盖execute
方法来创建new MDCAwareCompletableFuture<>().completeAsync(() -> {
getAcountDetails(user);
return null;
});
,以服务您的用例。你明白了!您可以在post中以相同的方式找到以上所有内容的详细说明。
那样,您的代码可能看起来像
animate