新增:作业提交项目提交;mdc日志追踪提效组件提交

This commit is contained in:
yangyang01000846
2025-11-12 13:41:29 +08:00
parent a1a329f6e3
commit 9eef32d9ea
39 changed files with 1939 additions and 9 deletions

View File

@@ -45,6 +45,15 @@
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
</dependency>
<!-- Spring Boot 3.x 适配Jakarta EE Servlet API仅编译期依赖 -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version> <!-- 与 Spring Boot 3.x 兼容,对应 Servlet 5.0 规范 -->
<scope>provided</scope> <!-- 关键:运行时由 web 模块的容器(如 Tomcat提供实现 -->
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>

View File

@@ -0,0 +1,19 @@
package com.sdm.common.common.mdc;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
@Component
public class FeignMdcRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
// 这个apply方法会在每次Feign客户端方法调用前执行
String traceId = MDC.get("traceId");
if (StringUtils.isNotBlank(traceId)) {
template.header("X-Trace-Id", traceId);
}
}
}

View File

@@ -0,0 +1,15 @@
package com.sdm.common.common.mdc;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class HttpFilterConfig {
@Bean
public FilterRegistrationBean<TraceIdFilter> traceIdFilter() {
FilterRegistrationBean<TraceIdFilter> bean = new FilterRegistrationBean<>(new TraceIdFilter());
bean.setOrder(1);
return bean;
}
}

View File

@@ -0,0 +1,23 @@
package com.sdm.common.common.mdc;
import org.slf4j.MDC;
import org.springframework.core.task.TaskDecorator;
import java.util.Map;
public class MdcTaskDecorator implements TaskDecorator {
@Override
public Runnable decorate(Runnable runnable) {
Map<String, String> contextMap = MDC.getCopyOfContextMap();
return () -> {
try {
if (contextMap != null) {
MDC.setContextMap(contextMap);
}
runnable.run();
} finally {
MDC.clear();
}
};
}
}

View File

@@ -0,0 +1,62 @@
package com.sdm.common.common.mdc;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.MDC;
import java.io.IOException;
import java.util.UUID;
// 拦截所有 HTTP 请求
public class TraceIdFilter implements Filter {
/**
* MDC 中存储 traceId 的 key需与日志格式中的 %X{traceId} 对应)
*/
public static final String TRACE_ID_KEY = "traceId";
/**
* 响应头中返回 traceId 的 key前端可通过此 key 获取)
*/
public static final String TRACE_ID_HEADER = "X-Trace-Id";
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
try {
// 1. 生成或获取 traceId优先从请求头获取支持前端传递便于联调
String traceId = request.getHeader(TRACE_ID_HEADER);
if (traceId == null || traceId.trim().isEmpty()) {
// 生成 UUID 并去除横杠32位简洁易读
traceId = UUID.randomUUID().toString().replace("-", "");
}
// 2. 存入 MDC供日志打印使用所有日志框架可通过 %X{traceId} 获取)
MDC.put(TRACE_ID_KEY, traceId);
// 3. 写入响应头(返回给前端,便于前端排查问题时匹配日志)
response.setHeader(TRACE_ID_HEADER, traceId);
// 4. 放行请求,继续执行后续过滤器/控制器
filterChain.doFilter(request, response);
} finally {
// 5. 清除 MDC 中的 traceId关键避免线程池复用导致的 traceId 污染)
MDC.remove(TRACE_ID_KEY);
}
}
// 初始化和销毁方法可空实现(无需额外操作)
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void destroy() {}
}

View File

@@ -0,0 +1,142 @@
package com.sdm.common.utils.log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
/**
* 核心业务日志工具类
* 特性:
* 1. 日志自动输出到 core.log
* 2. 打印原始调用类名、方法名和行号
* 3. 支持 SLF4J 标准日志方法info/error/debug 等)
*/
public class CoreLogger {
private static final Logger coreLog = LoggerFactory.getLogger("coreLogger");
private static final String CALLER_INFO_KEY = "callerInfo";
// 私有构造器
private CoreLogger() {
throw new AssertionError("工具类不允许实例化");
}
/**
* 获取原始调用位置信息(类名.方法名(行号)
* 跳过工具类自身的调用栈,定位到真实业务代码
*/
private static String getCallerInfo() {
// 获取当前线程调用栈
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
// 栈索引说明(根据实际调用链调整):
// 0: Thread.getStackTrace()
// 1: CoreLogger.getCallerInfo()
// 2: CoreLogger.xxx()(如 info/error 等工具方法)
// 3: 实际业务类的调用方法(目标位置)
if (stackTrace.length >= 4) {
StackTraceElement caller = stackTrace[3];
return String.format(
"%s.%s(%d)",
caller.getClassName(), // 原始调用类名
caller.getMethodName(), // 原始调用方法名
caller.getLineNumber() // 原始调用行号
);
}
// 异常情况:无法获取调用栈时返回默认值
return "UnknownCaller";
}
// -------------------- 以下为 SLF4J 标准日志方法封装 --------------------
public static void debug(String message) {
try {
MDC.put(CALLER_INFO_KEY, getCallerInfo());
coreLog.debug(message);
} finally {
MDC.remove(CALLER_INFO_KEY); // 清除 MDC避免线程复用污染
}
}
public static void debug(String message, Object... args) {
try {
MDC.put(CALLER_INFO_KEY, getCallerInfo());
coreLog.debug(message, args);
} finally {
MDC.remove(CALLER_INFO_KEY);
}
}
public static void info(String message) {
try {
MDC.put(CALLER_INFO_KEY, getCallerInfo());
coreLog.info(message);
} finally {
MDC.remove(CALLER_INFO_KEY);
}
}
public static void info(String message, Object... args) {
try {
MDC.put(CALLER_INFO_KEY, getCallerInfo());
coreLog.info(message, args);
} finally {
MDC.remove(CALLER_INFO_KEY);
}
}
public static void warn(String message) {
try {
MDC.put(CALLER_INFO_KEY, getCallerInfo());
coreLog.warn(message);
} finally {
MDC.remove(CALLER_INFO_KEY);
}
}
public static void warn(String message, Object... args) {
try {
MDC.put(CALLER_INFO_KEY, getCallerInfo());
coreLog.warn(message, args);
} finally {
MDC.remove(CALLER_INFO_KEY);
}
}
public static void warn(String message, Throwable throwable) {
try {
MDC.put(CALLER_INFO_KEY, getCallerInfo());
coreLog.warn(message, throwable);
} finally {
MDC.remove(CALLER_INFO_KEY);
}
}
public static void error(String message) {
try {
MDC.put(CALLER_INFO_KEY, getCallerInfo());
coreLog.error(message);
} finally {
MDC.remove(CALLER_INFO_KEY);
}
}
public static void error(String message, Object... args) {
try {
MDC.put(CALLER_INFO_KEY, getCallerInfo());
coreLog.error(message, args);
} finally {
MDC.remove(CALLER_INFO_KEY);
}
}
public static void error(String message, Throwable throwable) {
try {
MDC.put(CALLER_INFO_KEY, getCallerInfo());
coreLog.error(message, throwable);
} finally {
MDC.remove(CALLER_INFO_KEY);
}
}
}