liuchengsen 2 месяцев назад
Родитель
Сommit
425f7b6e47

+ 45 - 0
zhijiayun-common/src/main/java/com/xuekairui/common/TraceIdGenerator.java

@@ -0,0 +1,45 @@
+package com.xuekairui.common;
+
+import java.util.Random;
+
+/**
+ * 全链路 Trace ID 生成器
+ * <p>
+ * 提供统一的 traceId 生成规则,避免各过滤器/装饰器各自维护随机逻辑。
+ * 格式:8位随机字符(字母+数字,去掉易混淆的 0/O/1/I/o/l)
+ * 示例:6k3Fa9B2
+ * </p>
+ */
+public final class TraceIdGenerator {
+
+    private TraceIdGenerator() {
+        // 工具类禁止实例化
+    }
+
+    /** 随机字符集(大小写字母+数字,去掉易混淆的 0/O/1/I/o/l) */
+    private static final String ID_CHARS = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789";
+
+    private static final Random RANDOM = new Random();
+
+    private static final int SEGMENT_LENGTH = 4;
+    private static final int SEGMENT_COUNT = 2;
+
+    /**
+     * 生成新的 traceId
+     */
+    public static String generate() {
+        StringBuilder sb = new StringBuilder(SEGMENT_LENGTH * SEGMENT_COUNT);
+        for (int i = 0; i < SEGMENT_COUNT; i++) {
+            sb.append(randomSegment());
+        }
+        return sb.toString();
+    }
+
+    private static String randomSegment() {
+        StringBuilder sb = new StringBuilder(SEGMENT_LENGTH);
+        for (int i = 0; i < SEGMENT_LENGTH; i++) {
+            sb.append(ID_CHARS.charAt(RANDOM.nextInt(ID_CHARS.length())));
+        }
+        return sb.toString();
+    }
+}

+ 2 - 16
zhijiayun-gateway/src/main/java/com/xuekairui/gateway/filter/RequestLoggingFilter.java

@@ -1,5 +1,6 @@
 package com.xuekairui.gateway.filter;
 
+import com.xuekairui.common.TraceIdGenerator;
 import com.xuekairui.common.ValidateUtil;
 import com.xuekairui.gateway.config.GatewayProperties;
 import jakarta.servlet.FilterChain;
@@ -33,10 +34,6 @@ public class RequestLoggingFilter extends OncePerRequestFilter {
     private static final String TRACE_ID_KEY = "traceId";
     private static final String START_TIME_ATTR = "requestStartTime";
 
-    /** 随机字母+数字字符集(大小写字母+数字,去掉易混淆的 0/O/1/I/o/l) */
-    private static final String ID_CHARS = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789";
-    private static final java.util.Random RANDOM = new java.util.Random();
-
     @Override
     protected void doFilterInternal(
             @NonNull HttpServletRequest request,
@@ -113,19 +110,8 @@ public class RequestLoggingFilter extends OncePerRequestFilter {
 
     /**
      * 生成 Trace ID
-     * 格式:TXXXXXXXX(T前缀 + 8位随机,共9字符)
-     * 示例:T6k3Fa9B2
      */
     private String generateTraceId() {
-        return "T" + randomChars() + randomChars();
-    }
-
-    /** 生成4位随机字符串(字母+数字,去掉易混淆字符) */
-    private String randomChars() {
-        StringBuilder sb = new StringBuilder(4);
-        for (int i = 0; i < 4; i++) {
-            sb.append(ID_CHARS.charAt(RANDOM.nextInt(ID_CHARS.length())));
-        }
-        return sb.toString();
+        return TraceIdGenerator.generate();
     }
 }

+ 1 - 0
zhijiayun-gateway/src/main/resources/application-prod.yml

@@ -66,6 +66,7 @@ sms:
 logging:
   level:
     com.xuekairui: info
+    com.xuekairui.user.mapper: info    # 关闭 MyBatis SQL 调试日志(避免敏感参数泄露及性能损耗)
     org.springframework.security: warn
     com.zaxxer.hikari: warn
   file:

+ 0 - 3
zhijiayun-gateway/src/main/resources/logback-spring.xml

@@ -93,9 +93,6 @@
     <!-- Spring Security日志 -->
     <logger name="org.springframework.security" level="INFO"/>
     
-    <!-- MyBatis SQL日志 -->
-    <logger name="com.xuekairui.user.mapper" level="DEBUG"/>
-    
     <!-- 减少第三方库的日志噪音 -->
     <logger name="org.apache.ibatis" level="WARN"/>
     <logger name="com.zaxxer.hikari" level="INFO"/>

+ 31 - 0
zhijiayun-user/src/main/java/com/xuekairui/user/config/AsyncConfig.java

@@ -1,8 +1,12 @@
 package com.xuekairui.user.config;
 
+import com.xuekairui.common.TraceIdGenerator;
 import lombok.extern.slf4j.Slf4j;
+import org.jetbrains.annotations.NotNull;
+import org.slf4j.MDC;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.core.task.TaskDecorator;
 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
 
@@ -57,8 +61,35 @@ public class AsyncConfig {
         scheduler.setThreadNamePrefix("scheduling-");
         scheduler.setAwaitTerminationSeconds(60);
         scheduler.setWaitForTasksToCompleteOnShutdown(true);
+        scheduler.setTaskDecorator(new MdcTraceIdTaskDecorator());
         scheduler.setErrorHandler(t ->
                 log.error("定时任务执行异常", t));
         return scheduler;
     }
+
+    /**
+     * 定时任务 MDC 装饰器:为每个任务生成独立 traceId,便于无 HTTP 上下文时定位问题
+     */
+    public static class MdcTraceIdTaskDecorator implements TaskDecorator {
+
+        private static final String TRACE_ID_KEY = "traceId";
+
+        @NotNull
+        @Override
+        public Runnable decorate(@NotNull Runnable runnable) {
+            return () -> {
+                String traceId = generateTraceId();
+                MDC.put(TRACE_ID_KEY, traceId);
+                try {
+                    runnable.run();
+                } finally {
+                    MDC.clear();
+                }
+            };
+        }
+
+        private String generateTraceId() {
+            return TraceIdGenerator.generate();
+        }
+    }
 }

+ 10 - 1
zhijiayun-user/src/main/java/com/xuekairui/user/security/JwtAuthenticationFilter.java

@@ -14,6 +14,7 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
 import org.springframework.stereotype.Component;
+import org.slf4j.MDC;
 import org.springframework.util.StringUtils;
 import org.springframework.web.filter.OncePerRequestFilter;
 
@@ -71,6 +72,9 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
                     request.setAttribute("userId", userId);
                     request.setAttribute("phone", phone);
                     request.setAttribute("role", role);
+
+                    // 尽早注入用户ID到MDC,使链路中后续日志都能带上uid
+                    MDC.put("uid", String.valueOf(userId));
                 }
             } catch (Exception e) {
                 log.debug("JWT认证失败: {}", e.getMessage());
@@ -78,7 +82,12 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
             }
         }
 
-        filterChain.doFilter(request, response);
+        try {
+            filterChain.doFilter(request, response);
+        } finally {
+            // 清理当前过滤器注入的uid,避免跨请求泄漏
+            MDC.remove("uid");
+        }
     }
 
     private String extractToken(HttpServletRequest request) {