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

日志链路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

http://www.xdnf.cn/news/5950.html

相关文章:

  • 解锁 CPFR 潜力:电商智能补货优化算法的全链路设计与实战指南
  • 特征偏移、标签偏移、数量偏移、概念漂移分别是什么?
  • 不锈钢气动保温V型球阀:专为高粘度、颗粒介质设计的智能控温解决方案-耀圣
  • 【bag of n-grams】 N-gram词袋模型 简介
  • 物联网设备如何与互联网“牵手”
  • CSP认证准备第三天-差分及第36次CCF认证(BFS)
  • 第十七章:Llama Factory 深度剖析:易用性背后的微调框架设计
  • JavaScript实践(三)JavaScript序列化与反序列化深度解析
  • 线性投影层---将输入特征从一个空间映射到另一个空间
  • 【一次成功!】Ubuntu22.04安装cartographer
  • hashicorp vault机密管理系统的国产化替代:安当SMS凭据管理系统,量子安全赋能企业密钥管理
  • 数据擦除标准:1-Pass vs. 3-Pass vs. 7-Pass有什么区别,哪个更好?
  • mysql版本升级常见错误
  • 找客户软件如何实现精准定位?
  • 竞业禁止协议中AI技能限制的深度剖析
  • 【HT周赛】T3.二维平面 题解(分块:矩形chkmax,求矩形和)
  • 如何检测和解决服务器端口被占用的问题
  • #降级监听#PASS THE HASH常见的利用方式#Relay HASH
  • 嵌入式操作系统学习笔记
  • JAVA:synchronized总结
  • UDP和TCP协议
  • 硬件自动化测试平台搭建(硅后验证,非EDA)day1 大概了解
  • 化工行业专利管理系统:全流程解决方案解析
  • Java 反射机制(Reflection)
  • 京东让家政服务,从「开盲盒」到「开卷考」
  • jFinal 使用 SolonMCP 开发 MCP(拥抱新潮流)
  • .NET8关于ORM的一次思考
  • ELF文件详解
  • 基于 PLC 的轮式服务机器人研究
  • 数据归属地信息库在广告营销中的应用