亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

具有 CompletableFuture 的 MDC 記錄器

具有 CompletableFuture 的 MDC 記錄器

慕勒3428872 2022-10-26 15:50:19
我正在使用 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 : 12019-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 的任何解決方案。
查看完整描述

2 回答

?
PIPIONE

TA貢獻1829條經驗 獲得超9個贊

我的解決方案主題是(它可以與 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> {


    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看起來像(ForkJoinTask為簡單起見,跳過了帶參數的方法)


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 {

            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實例化了 self 版本,例如new CompletableFuture.... 對于此類方法(大多數公共靜態方法),請使用替代方法來獲取MDCAwareCompletableFuture. 使用替代方法的示例可能是,而不是使用CompletableFuture.supplyAsync(...),您可以選擇new MDCAwareCompletableFuture<>().completeAsync(...)

  • 當您因為某個外部庫返回CompletableFuture一個. 顯然,您不能在該庫中保留上下文,但是在您的代碼命中應用程序代碼后,此方法仍會保留上下文。MDCAwareCompletableFuturegetMDCAwareCompletionStageCompletableFuture

  • 在提供 executor 作為參數時,請確保它是 MDC Aware,例如MDCAwareForkJoinPool. 您也可以MDCAwareThreadPoolExecutor通過覆蓋execute方法創建以服務于您的用例。你明白了!

您可以在一篇關于相同內容的帖子中找到上述所有內容的詳細說明。

這樣,您的代碼可能看起來像

new MDCAwareCompletableFuture<>().completeAsync(() -> {
            getAcountDetails(user);
            return null;
        });


查看完整回答
反對 回復 2022-10-26
?
炎炎設計

TA貢獻1808條經驗 獲得超4個贊

創建包裝方法


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.


查看完整回答
反對 回復 2022-10-26
  • 2 回答
  • 0 關注
  • 254 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號