当前位置: 首页 > news >正文

日志链路ID配置,traceId多线程不打印什么鬼?

logback.xml 关键配置  [traceId:%X{traceId}]

    <!-- 彩色日志格式模板 --><property name="log.pattern.color"value="%green(%d{yyyy-MM-dd HH:mm:ss.SSS}) [%thread] %highlight(%-5level){FATAL=red, ERROR=red, WARN=yellow, INFO=green, DEBUG=cyan, TRACE=blue} [traceId:%X{traceId}] %cyan(%logger{36}) - %msg%n"/><!-- 普通日志格式模板(非彩色) --><property name="log.pattern.plain" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [traceId:%X{traceId}] %logger{36} - %msg%n"/>

过滤器配置

    @Bean@Order(Ordered.HIGHEST_PRECEDENCE + 100)@ConditionalOnMissingBean(value = TraceFilter.class)public FilterRegistrationBean <TraceFilter> traceFilterBean(){FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();filterRegistrationBean.setFilter(new TraceFilter());filterRegistrationBean.addUrlPatterns("/*");return filterRegistrationBean;}
package com.ldj.mybatisflex.filter;import com.ldj.mybatisflex.util.TraceIdUtil;
import lombok.extern.slf4j.Slf4j;import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;/*** User: ldj* Date: 2025/5/13* Time: 18:39* Description: 链路过滤器*/
@Slf4j
public class TraceFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {log.info(" Init trace filter ...");}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {try {HttpServletRequest httpServletRequest = (HttpServletRequest) request;// 获取从网关传过来的traceIdString gatewayTraceId = httpServletRequest.getHeader(TraceIdUtil.TRACE_ID);TraceIdUtil.checkAndSaveTraceId(gatewayTraceId);chain.doFilter(request, response);} catch (Exception e) {e.printStackTrace();}finally {TraceIdUtil.removeTraceId();}}@Overridepublic void destroy() {log.info(" Destroy trace filter ...");}
}

id工具类

package com.ldj.mybatisflex.util;import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;import java.util.UUID;/*** User: ldj* Date: 2025/5/13* Time: 14:37* Description: 链路id工具类*/
public class TraceIdUtil {public static final String TRACE_ID = "traceId";/*** 自动生成链路id*/public static String buildTraceId() {return UUID.randomUUID().toString().replaceAll("-", "");}/*** 移除链路id*/public static void removeTraceId() {MDC.remove(TRACE_ID);}/*** 检验生成链路id*/public static void checkAndSaveTraceId(String traceId) {MDC.put(TRACE_ID, StringUtils.isBlank(traceId) ? buildTraceId() : traceId);}/*** 获取链路id*/public static String getTraceId() {return MDC.get(TRACE_ID);}
}

统一响应类

package com.ldj.mybatisflex.common;import com.ldj.mybatisflex.util.TraceIdUtil;
import lombok.Getter;
import lombok.Setter;
import org.springframework.context.i18n.LocaleContextHolder;import java.util.ResourceBundle;/*** User: ldj* Date: 2025/1/12* Time: 18:08* Description: 统一响应类*/
@Getter
@Setter
public class ApiResponse<T> {private static final String basePath = "i18n/message";private static final Integer successCode = 200;private static final String success = "成功!";private static final Integer failCode = 500;private static final String fail = "失败!";private Integer code;private String message;// 过滤器会往MDC存入traceIdprivate String traceId;private T data;public static <T> ApiResponse<T> success(T data) {ApiResponse<T> apiResponse = new ApiResponse<>();apiResponse.setCode(200);apiResponse.setMessage(success);apiResponse.setTraceId(TraceIdUtil.getTraceId());apiResponse.setData(data);return apiResponse;}public static <T> ApiResponse<T> fail() {return fail(failCode, fail);}//关键代码是读取国际化的配置文件,作为错误提示消息public static <T> ApiResponse<T> fail(ExceptionEnum exceptionEnum, T date) {ApiResponse<T> apiResponse = new ApiResponse<>();apiResponse.setCode(exceptionEnum.getCode());ResourceBundle bundle = ResourceBundle.getBundle(basePath, LocaleContextHolder.getLocale());String message = bundle.getString(exceptionEnum.getCode().toString());apiResponse.setMessage(message);apiResponse.setTraceId(TraceIdUtil.getTraceId());apiResponse.setData(date);return apiResponse;}public static <T> ApiResponse<T> fail(Integer code, String message) {ApiResponse<T> apiResponse = new ApiResponse<>();apiResponse.setCode(code);apiResponse.setMessage(message);apiResponse.setTraceId(TraceIdUtil.getTraceId());apiResponse.setData(null);return apiResponse;}
}

测试效果:

后续补充:

1. 对于多线程程序,如何解决链路id不能在子线程里传递?

解决方法:

package com.ldj.mybatisflex.config;import org.slf4j.MDC;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;/*** User: ldj* Date: 2025/5/13* Time: 20:35* Description: 解决@Async 链路id不能在子线程传递的问题*/
@Configuration
public class AsyncConfig extends AsyncConfigurerSupport {@Overridepublic Executor getAsyncExecutor() {ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();taskExecutor.setCorePoolSize(8);taskExecutor.setMaxPoolSize(50);taskExecutor.setQueueCapacity(100);taskExecutor.setKeepAliveSeconds(5);taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());taskExecutor.setThreadNamePrefix("async-pool-");// 自定义处理增强方法taskExecutor.setTaskDecorator(task -> {// 获取主线程的 MDC 上下文(map 形式)Map<String, String> copyOfContextMap = MDC.getCopyOfContextMap();return () -> {try {// 如果存在上下文,则将主线程的 traceId 等信息传递给线程池的子线程if (!copyOfContextMap.isEmpty()) {MDC.setContextMap(copyOfContextMap);}task.run();} finally {MDC.clear();}};});taskExecutor.initialize();return taskExecutor;}}

 效果:

2. 对于消息队列,如何解决生产者端的链路id不能在消费者端传递?

解决思路: 消息发送前做增强,消息被消费时做增强,具体是有增强扩张点在扩展点做增强,或者期切mq相关注解拿到消息如Message。

我这里演示RocketMq,如果RabbatMq  发送消息时有前置函数,消费时可切@RabbitListener注解

拿到Message 从而拿到traceId,然后放入MDC。

发送端改造.

首先,我们需要创建一个 SendMessageHook 来在发送消息之前添加 traceId 到消息属性中。

public class TraceIdSendMessageHook implements SendMessageHook {@Overridepublic String hookName() {return "TraceIdSendMessageHook";}@Overridepublic void sendMessageBefore(SendMessageContext context) {// 从MDC获取traceIdString traceId = MDC.get("traceId");if (traceId == null || traceId.isEmpty()) {traceId = UUID.randomUUID().toString(); // 如果没有traceId,则生成一个新的MDC.put("traceId", traceId);}// 将traceId放入消息属性context.getMessage().putUserProperty("TRACE_ID", traceId);}@Overridepublic void sendMessageAfter(SendMessageContext context) {// 可以在此处进行发送后的处理,如日志记录等}
}

然后,在 Broker 的配置文件 broker.conf 中注册这个 Hook

消费端改造

接下来,我们需要在消费端从消息中提取 traceId 并设置到 MDC 中。为此,可以使用 ConsumeMessageHook

public class TraceIdConsumeMessageHook implements ConsumeMessageHook {@Overridepublic String hookName() {return "TraceIdConsumeMessageHook";}@Overridepublic void consumeMessageBefore(ConsumeMessageContext context) {Message message = context.getMessage();String traceId = message.getUserProperty("TRACE_ID");if (traceId != null) {MDC.put("traceId", traceId); // 设置到MDC}}@Overridepublic void consumeMessageAfter(ConsumeMessageContext context) {// 清理MDCMDC.clear();}
}

在 Broker 的配置文件 broker.conf 中注册2个Hook:

sendMessageHook=TraceIdSendMessageHook
consumeMessageHook=TraceIdConsumeMessageHook

相关文章:

  • 如何在sheel中运行spark
  • 驱动无忧,打印随心:统信推出全新驱动下载平台
  • 从代码学习深度学习 - 全卷积神经网络 PyTorch版
  • YOLOv11融合[AAAI2025]的PConv模块
  • 技术视角下的TikTok店铺运营:从0到1的5个关键点
  • Flask+HTML+Jquery 文件上传下载
  • DeepSeek 赋能汽车全生态:从产品到服务的智能化跃迁
  • supabase 怎么新建项目?
  • Oracle 在线日志文件和控制文件损坏处理思路
  • FedTracker:为联邦学习模型提供所有权验证和可追溯性
  • 黑马k8s(五)
  • javax.servlet.Filter 介绍-笔记
  • 邀请函|PostgreSQL培训认证报名正式开启
  • FFmpeg 与 C++ 构建音视频处理全链路实战(三)—— FFmpeg 内存模型
  • 什么情况会导致JVM退出?
  • 游戏引擎学习第275天:将旋转和剪切传递给渲染器
  • 基于TouchSocket实现WebSocket自定义OpCode扩展协议
  • 【Folium】使用离线地图
  • 百度导航广告“焊死”东鹏特饮:商业底线失守,用户安全成隐忧
  • 【NLP 72、Prompt、Agent、MCP、function calling】
  • 崔登荣任国家游泳队总教练
  • 横跨万里穿越百年,《受到召唤·敦煌》中张艺兴一人分饰两角
  • 受美关税影响,本田预计新财年净利下降七成,并推迟加拿大建厂计划
  • 郑培凯:汤显祖的“至情”与罗汝芳的“赤子之心”
  • 印称印巴军事行动总指挥同意将局势降级
  • 警方通报男子地铁上拍视频致乘客恐慌受伤:列车运行一度延误,已行拘