diff --git a/fund-common/src/main/java/com/fundplatform/common/feign/FeignChainInterceptor.java b/fund-common/src/main/java/com/fundplatform/common/feign/FeignChainInterceptor.java
index df1ea601..68ffa47 100644
--- a/fund-common/src/main/java/com/fundplatform/common/feign/FeignChainInterceptor.java
+++ b/fund-common/src/main/java/com/fundplatform/common/feign/FeignChainInterceptor.java
@@ -5,6 +5,7 @@ import com.fundplatform.common.context.TraceContextHolder;
import com.fundplatform.common.context.UserContextHolder;
import feign.RequestInterceptor;
import feign.RequestTemplate;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
@@ -12,6 +13,8 @@ import org.springframework.stereotype.Component;
*
*
职责:在通过 OpenFeign 发起下游 HTTP 请求时,统一透传:
*
+ * - X-Source-Service:请求来源服务名称(调用方标识);
+ * - X-Request-Time:请求发起时间戳(毫秒);
* - X-Tenant-Id:租户ID;
* - X-Uid:当前用户ID;
* - X-Uname:当前用户名;
@@ -21,20 +24,34 @@ import org.springframework.stereotype.Component;
@Component
public class FeignChainInterceptor implements RequestInterceptor {
+ public static final String HEADER_SOURCE_SERVICE = "X-Source-Service";
+ public static final String HEADER_REQUEST_TIME = "X-Request-Time";
public static final String HEADER_TENANT_ID = "X-Tenant-Id";
public static final String HEADER_UID = "X-Uid";
public static final String HEADER_UNAME = "X-Uname";
public static final String HEADER_TRACE_ID = "X-Trace-Id";
+ /**
+ * 当前服务名称(请求来源标识)
+ */
+ @Value("${spring.application.name:unknown}")
+ private String serviceName;
+
@Override
public void apply(RequestTemplate template) {
- // 多租户透传
+ // 1. 请求来源服务标识(调用方)
+ template.header(HEADER_SOURCE_SERVICE, serviceName);
+
+ // 2. 请求发起时间戳(用于计算链路耗时)
+ template.header(HEADER_REQUEST_TIME, String.valueOf(System.currentTimeMillis()));
+
+ // 3. 多租户透传
Long tenantId = TenantContextHolder.getTenantId();
if (tenantId != null) {
template.header(HEADER_TENANT_ID, String.valueOf(tenantId));
}
- // 用户信息透传
+ // 4. 用户信息透传
Long uid = UserContextHolder.getUserId();
if (uid != null) {
template.header(HEADER_UID, String.valueOf(uid));
@@ -44,7 +61,7 @@ public class FeignChainInterceptor implements RequestInterceptor {
template.header(HEADER_UNAME, uname);
}
- // TraceId 透传(如不存在则生成)
+ // 5. TraceId 透传(如不存在则生成)
String traceId = TraceContextHolder.getOrCreateTraceId();
template.header(HEADER_TRACE_ID, traceId);
}
diff --git a/fund-common/src/main/java/com/fundplatform/common/web/ContextInterceptor.java b/fund-common/src/main/java/com/fundplatform/common/web/ContextInterceptor.java
index 8aa42de..d44f134 100644
--- a/fund-common/src/main/java/com/fundplatform/common/web/ContextInterceptor.java
+++ b/fund-common/src/main/java/com/fundplatform/common/web/ContextInterceptor.java
@@ -5,22 +5,59 @@ import com.fundplatform.common.context.TraceContextHolder;
import com.fundplatform.common.context.UserContextHolder;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
/**
* 租户和用户上下文拦截器
- * 从HTTP Header中提取租户ID和用户ID,设置到ThreadLocal中
+ * 从HTTP Header中提取租户ID、用户ID、请求来源等信息,设置到ThreadLocal中
+ * 同时记录请求来源和链路耗时信息
*/
@Component
public class ContextInterceptor implements HandlerInterceptor {
+ private static final Logger logger = LoggerFactory.getLogger(ContextInterceptor.class);
+
+ public static final String HEADER_SOURCE_SERVICE = "X-Source-Service";
+ public static final String HEADER_REQUEST_TIME = "X-Request-Time";
public static final String HEADER_TENANT_ID = "X-Tenant-Id";
public static final String HEADER_USER_ID = "X-User-Id";
public static final String HEADER_TRACE_ID = "X-Trace-Id";
+ /**
+ * 请求开始时间属性Key
+ */
+ private static final String ATTR_START_TIME = "requestStartTime";
+
+ /**
+ * 当前服务名称
+ */
+ @Value("${spring.application.name:unknown}")
+ private String serviceName;
+
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
+ // 记录请求开始时间
+ request.setAttribute(ATTR_START_TIME, System.currentTimeMillis());
+
+ // 提取请求来源服务
+ String sourceService = request.getHeader(HEADER_SOURCE_SERVICE);
+ String requestTimeStr = request.getHeader(HEADER_REQUEST_TIME);
+
+ // 计算链路耗时(如果有请求发起时间)
+ Long chainDuration = null;
+ if (requestTimeStr != null && !requestTimeStr.isEmpty()) {
+ try {
+ long requestTime = Long.parseLong(requestTimeStr);
+ chainDuration = System.currentTimeMillis() - requestTime;
+ } catch (NumberFormatException e) {
+ // 忽略无效的时间戳
+ }
+ }
+
// 提取租户ID
String tenantIdStr = request.getHeader(HEADER_TENANT_ID);
if (tenantIdStr != null && !tenantIdStr.isEmpty()) {
@@ -42,18 +79,43 @@ public class ContextInterceptor implements HandlerInterceptor {
// 忽略无效的用户 ID
}
}
-
+
// 提取 TraceId(如不存在,后续会在需要时自动生成)
String traceId = request.getHeader(HEADER_TRACE_ID);
if (traceId != null && !traceId.isEmpty()) {
TraceContextHolder.setTraceId(traceId);
}
-
+
+ // 记录请求追踪日志
+ if (sourceService != null && chainDuration != null) {
+ logger.info("[Trace] {} -> {} | traceId={} | chainTime={}ms | path={}",
+ sourceService, serviceName, traceId, chainDuration, request.getRequestURI());
+ } else if (sourceService != null) {
+ logger.info("[Trace] {} -> {} | traceId={} | path={}",
+ sourceService, serviceName, traceId, request.getRequestURI());
+ }
+
return true;
}
@Override
- public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
+ public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
+ Object handler, Exception ex) {
+ // 计算当前服务处理耗时
+ Long startTime = (Long) request.getAttribute(ATTR_START_TIME);
+ if (startTime != null) {
+ long duration = System.currentTimeMillis() - startTime;
+ String traceId = request.getHeader(HEADER_TRACE_ID);
+ String sourceService = request.getHeader(HEADER_SOURCE_SERVICE);
+
+ logger.debug("[Trace] {} -> {} | traceId={} | processTime={}ms | status={}",
+ sourceService != null ? sourceService : "external",
+ serviceName,
+ traceId,
+ duration,
+ response.getStatus());
+ }
+
// 清理 ThreadLocal,防止内存泄漏
TenantContextHolder.clear();
UserContextHolder.clear();