欧洲乱码伦视频免费国产-麻豆国产精品专区-无码人妻一区二区三区精品视频-日韩免费一区二区三区超清视频-狠狠躁夜夜躁人人爽超碰97香蕉-日韩精品一区二区三区色欲av-久久人人爽av亚洲精品天堂-激情五月开心综合亚洲-久久精品无码一区二区综合好-开心五月四房播播
基于Slf4j的MDC实现日志链路串联-凤凰电竞官方网站

基于Slf4j的MDC实现日志链路串联

2026-02-04 14:17:18投稿人:摩杰平臺(tái)株洲(遼源)有限公司圍觀1272363 評(píng)論

基于Slf4j的MDC實(shí)現(xiàn)日志鏈路串聯(lián)

一 、問題背景

為了方便運(yùn)維日常定位排查問題時(shí),使用traceId查詢?nèi)罩緯r(shí)可以完整的查看當(dāng)前業(yè)務(wù)請(qǐng)求的完整日志鏈路 ,需使用traceId(日志跟蹤號(hào))將日志串聯(lián)起來 。

系統(tǒng)采用LogBack日志組件,在輸出日志時(shí)輸出_traceId日志跟蹤號(hào) ,logback.xml配置如下 :

../log/${ ServerName}/${ AppName}.log../log/${ ServerName}/${ AppName}.log.%d{ yyyy-MM-dd}        7%d{ HH:mm:ss.SSS} %-5level [%.15thread][%X{ _traceId}] %logger{ 36} - %.-4096msg%n

公司系統(tǒng)采用分布式架構(gòu) ,從接收到請(qǐng)求到業(yè)務(wù)處理完成并返回,涉及SpringMVC入口、服務(wù)間dubbo調(diào)用  、基于RocketMQ實(shí)現(xiàn)的業(yè)務(wù)解耦、以及來自第三方渠道的http通知回調(diào)。

現(xiàn)在的問題是從業(yè)務(wù)請(qǐng)求入口到返回業(yè)務(wù)響應(yīng),如何使用同一traceId將日志做串聯(lián) ?

Slf4j的MDC機(jī)制提供了上述問題的解決方案。

二、解決方案

在介紹具體解決方案之前,首先介紹下Slf4j日志框架的MDC機(jī)制 。映射診斷上下文(Mapped Diagnostic Context,簡稱MDC)可以簡單理解為當(dāng)前日志線程的上下文,也是一個(gè)k-v格式的map結(jié)構(gòu) 。

當(dāng)服務(wù)器幾乎同時(shí)處理多個(gè)請(qǐng)求時(shí) ,日志輸出通常是交錯(cuò)的,而MDC是基于每個(gè)線程進(jìn)行管理的,子線程自動(dòng)繼承其父線程的MDC的副本??梢酝ㄟ^往MDC里塞入traceId,達(dá)到串聯(lián)日志的目的 。

以下為MDC填充獲取traceId方法 :

public final static String TRACE_ID = "_traceId";static private String generateTraceId() {     return UUID.randomUUID().toString().replaceAll("-", "");}public static String getTraceId() {     return MDC.get(TRACE_ID);}public static void setTraceId(String traceId) {     MDC.put(TRACE_ID, traceId);}public static void clearTrace() {     MDC.remove(TRACE_ID);}public static void initTrace() {     String traceId = generateTraceId();    setTraceId(traceId);}

2.1 、接收http請(qǐng)求時(shí)的日志鏈路串聯(lián)

解決思路:可以從HttpServletRequest獲取traceId并塞到MDC中 。

具體如下:

public static void initTraceFromRequest(HttpServletRequest request) {     String traceId;    // 從前端表單提交里獲取traceId    traceId = request.getParameter(TRACE_ID);    if (StringUtils.isNotBlank(traceId)) {         setTraceId(traceId, request);        return;    }    // 從forward后端轉(zhuǎn)發(fā)請(qǐng)求里獲取traceId    traceId = (String) request.getAttribute(TRACE_ID);    if (StringUtils.isNotBlank(traceId)) {         setTraceId(traceId, request);        return;    }    // 從請(qǐng)求url路徑中里獲取traceId    Map pathVariables = (Map) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);    if (pathVariables != null) {         traceId = (String) pathVariables.get(TRACE_ID);    }    if (StringUtils.isNotBlank(traceId)) {         setTraceId(traceId, request);        return;    }    // 以上都獲取不到的話,就生成一個(gè)traceId并填充到MDC以及Session中    traceId = generateTraceId();    setTraceId(traceId, request);}

具體解決方案 :使用SpringMVC攔截器機(jī)制攔截所有web請(qǐng)求 ,獲取并串聯(lián)traceId。

public class LogTraceInterceptor extends HandlerInterceptorAdapter {     @Autowired    private ConfigBean configBean;    @Override    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {         if(handler instanceof HandlerMethod) {             TraceUtil.initTraceFrom(request);        }        return true;    }    @Override    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {     }}

實(shí)際測試時(shí)發(fā)現(xiàn)如下問題 :

后臺(tái)controller間有redirect重定向或forward轉(zhuǎn)發(fā)跳轉(zhuǎn)時(shí),跳轉(zhuǎn)前后的日志traceId沒有串聯(lián)上 。

分析如下 :