新增:作业提交项目提交;mdc日志追踪提效组件提交
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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() {}
|
||||
}
|
||||
142
common/src/main/java/com/sdm/common/utils/log/CoreLogger.java
Normal file
142
common/src/main/java/com/sdm/common/utils/log/CoreLogger.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user