Pre Merge pull request !823 from miracle-bean/dev

This commit is contained in:
miracle-bean 2026-01-15 08:44:48 +00:00 committed by Gitee
commit c237c3514a
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
4 changed files with 147 additions and 2 deletions

View File

@ -2,8 +2,8 @@
<configuration>
<property name="log.path" value="./logs"/>
<property name="console.log.pattern"
value="%cyan(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/>
<property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"/>
value="%cyan(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) %magenta([traceId:%X{traceId:-}]) - %msg%n"/>
<property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%X{traceId:-}] - %msg%n"/>
<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">

View File

@ -3,6 +3,7 @@ package org.dromara.common.web.config;
import jakarta.servlet.DispatcherType;
import org.dromara.common.web.config.properties.XssProperties;
import org.dromara.common.web.filter.RepeatableFilter;
import org.dromara.common.web.filter.TraceIdFilter;
import org.dromara.common.web.filter.XssFilter;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@ -10,6 +11,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.boot.web.servlet.FilterRegistration;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.core.Ordered;
/**
* Filter配置
@ -38,4 +40,15 @@ public class FilterConfig {
return new RepeatableFilter();
}
@Bean
public FilterRegistrationBean<TraceIdFilter> traceIdFilterRegistration() {
FilterRegistrationBean<TraceIdFilter> registration = new FilterRegistrationBean<>();
registration.setDispatcherTypes(DispatcherType.REQUEST);
registration.setFilter(new TraceIdFilter());
registration.addUrlPatterns("/*");
registration.setName("traceIdFilter");
registration.setOrder(Ordered.HIGHEST_PRECEDENCE + 10);
return registration;
}
}

View File

@ -0,0 +1,89 @@
package org.dromara.common.web.context;
import cn.hutool.core.util.IdUtil;
import org.slf4j.MDC;
import java.util.Map;
/**
* TraceId 上下文工具类基于 MDC + ThreadLocal 双保险
*/
public final class TraceContext {
private static final String TRACE_ID_KEY = "traceId";
private TraceContext() {
}
/**
* 获取当前 traceId不存在返回 null
*/
public static String getTraceId() {
return MDC.get(TRACE_ID_KEY);
}
/**
* 获取或创建 traceId不存在时自动生成
*/
public static String getOrCreateTraceId() {
String traceId = getTraceId();
if (traceId == null) {
traceId = generateTraceId();
setTraceId(traceId);
}
return traceId;
}
/**
* 设置 traceId MDC
*/
public static void setTraceId(String traceId) {
if (traceId != null) {
MDC.put(TRACE_ID_KEY, traceId);
}
}
/**
* 清理当前线程的 traceId非常重要
*/
public static void clear() {
MDC.remove(TRACE_ID_KEY);
}
/**
* 生成 traceId 的规则
*/
public static String generateTraceId() {
return IdUtil.getSnowflake().nextIdStr();
}
/**
* 捕获当前线程的完整 MDC 上下文快照
* <p>适用于手动创建 ThreadCompletableFuture自定义线程池等场景</p>
*/
public static Map<String, String> captureMdc() {
return MDC.getCopyOfContextMap();
}
/**
* 在新线程/任务中恢复完整的 MDC 上下文
* <p>建议在新线程的开头调用 finally 块中调用 clear()</p>
*/
public static void restoreMdc() {
Map<String, String> contextMap = TraceContext.captureMdc();
if (contextMap != null) {
MDC.setContextMap(contextMap);
}
}
/**
* 在新线程/任务中只恢复 traceId
*/
public static void restoreTraceId(String traceId) {
MDC.remove(TRACE_ID_KEY);
if (traceId != null) {
MDC.put(TRACE_ID_KEY, traceId);
}
}
}

View File

@ -0,0 +1,43 @@
package org.dromara.common.web.filter;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.dromara.common.web.context.TraceContext;
import org.springframework.util.StringUtils;
import java.io.IOException;
/**
* Author: miracle
* Date: 2026/1/14 下午1:41
*/
public class TraceIdFilter implements Filter {
private static final String TRACE_ID_HEADER = "X-Trace-Id";
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
String traceId = req.getHeader(TRACE_ID_HEADER);
// 前端传了就用前端的没传就自己生成
if (!StringUtils.hasText(traceId)) {
traceId = TraceContext.generateTraceId();
}
try {
TraceContext.setTraceId(traceId);
// 可选 traceId 写回响应头方便前端/测试工具看到
resp.setHeader(TRACE_ID_HEADER, traceId);
chain.doFilter(request, response);
} finally {
// 必须清理防止线程池复用导致上下文泄漏
TraceContext.clear();
}
}
}