SpringBoot+slf4j線程池全鏈路調(diào)用日志跟蹤問(wèn)題及解決思路(二)
本項(xiàng)目源碼已在多個(gè)項(xiàng)目中實(shí)踐
接著上一篇文章,項(xiàng)目中使用了線程池,那么子線程中日志就會(huì)丟失traceId,下面講解如何實(shí)現(xiàn)子線程中的traceId日志跟蹤。
解決思路
子線程在打印日志的過(guò)程中traceId將丟失,解決方式為重寫(xiě)線程池,將主線程的traceId繼續(xù)傳遞到子線程中。當(dāng)然,對(duì)于直接new創(chuàng)建線程的情況不考略【實(shí)際應(yīng)用中應(yīng)該避免這種用法】。
繼承ThreadPoolExecutor,重寫(xiě)執(zhí)行任務(wù)的方法
public final class OverrideThreadPoolExecutor extends ThreadPoolExecutor { @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())); } }
封裝ThreadMdcUtil工具類
以封裝Callable為例:
- 判斷當(dāng)前線程對(duì)應(yīng)MDC的Map是否存在,如果存在則設(shè)置子線程的ContextMap為當(dāng)前線程的;
- 如果不存在,則重新生成traceId;
- 執(zhí)行run方法
public final class ThreadMdcUtil { public static void setTraceIdIfAbsent() { if (MDC.get(TraceConstant.MDC_TRACE) == null || MDC.get(TraceConstant.MDC_TRACE).length() == 0) { String tid = UUID.randomUUID().toString().replace("-", ""); MDC.put(TraceConstant.MDC_TRACE, tid); } } 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); } setTraceIdIfAbsent(); try { return callable.call(); } finally { MDC.clear(); } }; } }
測(cè)試子線程中traceId的傳遞,本項(xiàng)目中ExecutorService已經(jīng)重寫(xiě)了線程池
@RestController @RequestMapping("trace") @Slf4j @AllArgsConstructor public class TestTraceController { private final ExecutorService executorService; @GetMapping("traceLog") public String traceLog() { log.info("---接口調(diào)用了---"); traceService(); asyncTrace(); return "success"; } private void traceService(){ log.error("## 執(zhí)行traceService方法"); } private void asyncTrace(){ CompletableFuture.runAsync(()->{ log.info("執(zhí)行線程池中的方法asyncTrace,未重寫(xiě)了線程池"); }, executorService); } }
未重寫(xiě)ThreadPoolExecutor效果如下:
重寫(xiě)ThreadPoolExecutor效果如下:
到此這篇關(guān)于SpringBoot+slf4j線程池全鏈路調(diào)用日志跟蹤的文章就介紹到這了,更多相關(guān)SpringBoot全鏈路調(diào)用日志跟蹤內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
對(duì)數(shù)據(jù)進(jìn)行分頁(yè)顯示到table中的實(shí)現(xiàn)方法
這篇文章主要介紹了對(duì)數(shù)據(jù)進(jìn)行分頁(yè)顯示到table中的實(shí)現(xiàn)方法的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-05-05springboot實(shí)現(xiàn)圖片大小壓縮功能
這篇文章主要為大家詳細(xì)介紹了springboot實(shí)現(xiàn)圖片大小壓縮功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04時(shí)間處理函數(shù)工具分享(時(shí)間戳計(jì)算)
這篇文章主要介紹了時(shí)間處理函數(shù)工具,包括得到時(shí)間戳、周一、周末、時(shí)間更改、時(shí)間精確計(jì)算等功能2014-01-01java實(shí)現(xiàn)飛機(jī)大戰(zhàn)小游戲
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)飛機(jī)大戰(zhàn)小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06Java使用抽象工廠模式實(shí)現(xiàn)的肯德基消費(fèi)案例詳解
這篇文章主要介紹了Java使用抽象工廠模式實(shí)現(xiàn)的肯德基消費(fèi)案例,較為詳細(xì)的分析了抽象工廠模式的定義、原理并結(jié)合實(shí)例形式分析了Java使用抽象工廠模式實(shí)現(xiàn)肯德基消費(fèi)案例的步驟與相關(guān)操作技巧,需要的朋友可以參考下2018-05-05基于RecyclerChart的KLine繪制Volume實(shí)現(xiàn)詳解
這篇文章主要為大家介紹了基于RecyclerChart的KLine繪制Volume實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03druid ParserException類錯(cuò)誤問(wèn)題及解決
這篇文章主要介紹了druid ParserException類錯(cuò)誤問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12MyBatis-Plus+達(dá)夢(mèng)數(shù)據(jù)庫(kù)實(shí)現(xiàn)高效數(shù)據(jù)持久化的示例
這篇文章主要介紹了MyBatis-Plus和達(dá)夢(mèng)數(shù)據(jù)庫(kù)實(shí)現(xiàn)高效數(shù)據(jù)持久化,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-08-08詳解jdbc實(shí)現(xiàn)對(duì)CLOB和BLOB數(shù)據(jù)類型的操作
這篇文章主要介紹了詳解jdbc實(shí)現(xiàn)對(duì)CLOB和BLOB數(shù)據(jù)類型的操作的相關(guān)資料,這里實(shí)現(xiàn)寫(xiě)入操作與讀寫(xiě)操作,需要的朋友可以參考下2017-08-08