具有 CompletableFuture 的 MDC 记录器

我正在使用 MDC Logger,除了一种情况外,它对我来说非常有用。在我们使用 CompletableFuture 的代码中,对于创建的线程,MDC 数据不会传递到下一个线程,因此日志失败。例如,在我使用下面的代码片段创建新线程的代码中。


CompletableFuture.runAsync(() -> getAcountDetails(user));

日志结果如下


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.


我试过下面的链接


http://shengwangi.blogspot.com/2015/09/using-log-mdc-in-multi-thread-helloworld-example.html?_sm_au_=iVVrZDSwwf0vP6MR


这非常适合TaskExecutor。但我还没有找到 CompletableFuture 的任何解决方案。


慕勒3428872
浏览 224回答 2
2回答

PIPIONE

我的解决方案主题是(它可以与 JDK 9+ 一起使用,因为自该版本以来公开了几个可覆盖的方法)让整个生态系统了解 MDC为此,我们需要解决以下场景:我们什么时候从这个类中获得 CompletableFuture 的新实例?→ 我们需要返回相同的 MDC 感知版本。我们什么时候从这个类之外获得 CompletableFuture 的新实例?→ 我们需要返回相同的 MDC 感知版本。在 CompletableFuture 类中使用哪个执行器?→ 在任何情况下,我们都需要确保所有执行者都了解 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> {&nbsp; &nbsp; public static final ExecutorService MDC_AWARE_ASYNC_POOL = new MDCAwareForkJoinPool();&nbsp; &nbsp; @Override&nbsp; &nbsp; public CompletableFuture newIncompleteFuture() {&nbsp; &nbsp; &nbsp; &nbsp; return new MDCAwareCompletableFuture();&nbsp; &nbsp; }&nbsp; &nbsp; @Override&nbsp; &nbsp; public Executor defaultExecutor() {&nbsp; &nbsp; &nbsp; &nbsp; return MDC_AWARE_ASYNC_POOL;&nbsp; &nbsp; }&nbsp; &nbsp; public static <T> CompletionStage<T> getMDCAwareCompletionStage(CompletableFuture<T> future) {&nbsp; &nbsp; &nbsp; &nbsp; return new MDCAwareCompletableFuture<>()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .completeAsync(() -> null)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .thenCombineAsync(future, (aVoid, value) -> value);&nbsp; &nbsp; }&nbsp; &nbsp; public static <T> CompletionStage<T> getMDCHandledCompletionStage(CompletableFuture<T> future,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Function<Throwable, T> throwableFunction) {&nbsp; &nbsp; &nbsp; &nbsp; Map<String, String> contextMap = MDC.getCopyOfContextMap();&nbsp; &nbsp; &nbsp; &nbsp; return getMDCAwareCompletionStage(future)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .handle((value, throwable) -> {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; setMDCContext(contextMap);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (throwable != null) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return throwableFunction.apply(throwable);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return value;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; });&nbsp; &nbsp; }}该类MDCAwareForkJoinPool看起来像(ForkJoinTask为简单起见,跳过了带参数的方法)public class MDCAwareForkJoinPool extends ForkJoinPool {&nbsp; &nbsp; //Override constructors which you need&nbsp; &nbsp; @Override&nbsp; &nbsp; public <T> ForkJoinTask<T> submit(Callable<T> task) {&nbsp; &nbsp; &nbsp; &nbsp; return super.submit(MDCUtility.wrapWithMdcContext(task));&nbsp; &nbsp; }&nbsp; &nbsp; @Override&nbsp; &nbsp; public <T> ForkJoinTask<T> submit(Runnable task, T result) {&nbsp; &nbsp; &nbsp; &nbsp; return super.submit(wrapWithMdcContext(task), result);&nbsp; &nbsp; }&nbsp; &nbsp; @Override&nbsp; &nbsp; public ForkJoinTask<?> submit(Runnable task) {&nbsp; &nbsp; &nbsp; &nbsp; return super.submit(wrapWithMdcContext(task));&nbsp; &nbsp; }&nbsp; &nbsp; @Override&nbsp; &nbsp; public void execute(Runnable task) {&nbsp; &nbsp; &nbsp; &nbsp; super.execute(wrapWithMdcContext(task));&nbsp; &nbsp; }}包装的实用方法如下public static <T> Callable<T> wrapWithMdcContext(Callable<T> task) {&nbsp; &nbsp; //save the current MDC context&nbsp; &nbsp; Map<String, String> contextMap = MDC.getCopyOfContextMap();&nbsp; &nbsp; return () -> {&nbsp; &nbsp; &nbsp; &nbsp; setMDCContext(contextMap);&nbsp; &nbsp; &nbsp; &nbsp; try {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return task.call();&nbsp; &nbsp; &nbsp; &nbsp; } finally {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // once the task is complete, clear MDC&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; MDC.clear();&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; };}public static Runnable wrapWithMdcContext(Runnable task) {&nbsp; &nbsp; //save the current MDC context&nbsp; &nbsp; Map<String, String> contextMap = MDC.getCopyOfContextMap();&nbsp; &nbsp; return () -> {&nbsp; &nbsp; &nbsp; &nbsp; setMDCContext(contextMap);&nbsp; &nbsp; &nbsp; &nbsp; try {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; task.run();&nbsp; &nbsp; &nbsp; &nbsp; } finally {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // once the task is complete, clear MDC&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; MDC.clear();&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; };}public static void setMDCContext(Map<String, String> contextMap) {&nbsp; &nbsp;MDC.clear();&nbsp; &nbsp;if (contextMap != null) {&nbsp; &nbsp; &nbsp; &nbsp;MDC.setContextMap(contextMap);&nbsp; &nbsp; }}以下是一些使用指南:使用类MDCAwareCompletableFuture而不是类CompletableFuture。类中的几个方法CompletableFuture实例化了 self 版本,例如new CompletableFuture....&nbsp;对于此类方法(大多数公共静态方法),请使用替代方法来获取MDCAwareCompletableFuture.&nbsp;使用替代方法的示例可能是,而不是使用CompletableFuture.supplyAsync(...),您可以选择new MDCAwareCompletableFuture<>().completeAsync(...)当您因为某个外部库返回CompletableFuture一个.&nbsp;显然,您不能在该库中保留上下文,但是在您的代码命中应用程序代码后,此方法仍会保留上下文。MDCAwareCompletableFuturegetMDCAwareCompletionStageCompletableFuture在提供 executor 作为参数时,请确保它是 MDC Aware,例如MDCAwareForkJoinPool.&nbsp;您也可以MDCAwareThreadPoolExecutor通过覆盖execute方法创建以服务于您的用例。你明白了!您可以在一篇关于相同内容的帖子中找到上述所有内容的详细说明。这样,您的代码可能看起来像new&nbsp;MDCAwareCompletableFuture<>().completeAsync(()&nbsp;->&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;getAcountDetails(user); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;null; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});

炎炎设计

创建包装方法static CompletableFuture<Void> myMethod(Runnable runnable) {&nbsp; &nbsp; Map<String, String> previous = MDC.getCopyOfContextMap();&nbsp; &nbsp; return CompletableFuture.runAsync(() -> {&nbsp; &nbsp; &nbsp; &nbsp; MDC.setContextMap(previous);&nbsp; &nbsp; &nbsp; &nbsp; try {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; runnable.run();&nbsp; &nbsp; &nbsp; &nbsp; } finally {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; MDC.clear();&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; });}并使用它代替CompletableFuture.runAsync.
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java