新增:mdc工具封装

This commit is contained in:
2026-01-28 19:19:45 +08:00
parent 87597670c2
commit 4393d4d87f
5 changed files with 131 additions and 38 deletions

View File

@@ -0,0 +1,18 @@
package com.sdm.common.common;
/*spdm 常量字符串定义类 */
public class SpdmCommonConstant {
/**
* 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";
}

View File

@@ -1,5 +1,6 @@
package com.sdm.common.filter;
import com.sdm.common.common.SpdmCommonConstant;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@@ -13,15 +14,7 @@ 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)
@@ -32,14 +25,14 @@ public class TraceIdFilter implements Filter {
try {
// 1. 生成或获取 traceId优先从请求头获取支持前端传递便于联调
String traceId = request.getHeader(TRACE_ID_HEADER);
String traceId = request.getHeader(SpdmCommonConstant.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);
MDC.put(SpdmCommonConstant.TRACE_ID_KEY, traceId);
// 3. 写入响应头(返回给前端,便于前端排查问题时匹配日志)
// response.setHeader(TRACE_ID_HEADER, traceId);
@@ -49,7 +42,7 @@ public class TraceIdFilter implements Filter {
} finally {
// 5. 清除 MDC 中的 traceId关键避免线程池复用导致的 traceId 污染)
MDC.remove(TRACE_ID_KEY);
MDC.remove(SpdmCommonConstant.TRACE_ID_KEY);
}
}

View File

@@ -0,0 +1,56 @@
package com.sdm.common.utils;
import com.sdm.common.common.SpdmCommonConstant;
import org.slf4j.MDC;
import java.util.UUID;
public class MdcUtil {
/**
* 私有构造方法,禁止实例化工具类
*/
private MdcUtil() {
throw new UnsupportedOperationException("工具类禁止实例化");
}
/**
* 生成并存入 traceId 到 MDC
* @return 生成的 traceId 字符串无横线的UUID
*/
public static String generateAndPutTraceId() {
// 生成无横线的UUID作为traceId
String traceId = UUID.randomUUID().toString().replace("-", "");
// 存入MDC供日志框架通过 %X{traceId} 获取
MDC.put(SpdmCommonConstant.TRACE_ID_KEY, traceId);
return traceId;
}
/**
* 从 MDC 中移除 traceId销毁
*/
public static void removeTraceId() {
MDC.remove(SpdmCommonConstant.TRACE_ID_KEY);
}
/**
* 获取当前 MDC 中的 traceId
* @return 当前traceId若不存在则返回null
*/
public static String getTraceId() {
return MDC.get(SpdmCommonConstant.TRACE_ID_KEY);
}
/**
* 手动存入指定的 traceId 到 MDC适用于需要复用traceId的场景如跨服务调用
* @param traceId 自定义的traceId
*/
public static void putTraceId(String traceId) {
if (traceId == null || traceId.trim().isEmpty()) {
throw new IllegalArgumentException("traceId 不能为空");
}
MDC.put(SpdmCommonConstant.TRACE_ID_KEY, traceId);
}
}

View File

@@ -4,6 +4,7 @@ import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.sdm.common.common.SdmResponse;
import com.sdm.common.common.ThreadLocalContext;
import com.sdm.common.utils.MdcUtil;
import com.sdm.project.service.ILyricInternalService;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
@@ -23,21 +24,26 @@ public class LyricTodoListSchedule {
// xxljob平台配置定时任务
@XxlJob("lyricTodoListHandler")
public void hpcJobStatusHandler() throws Exception {
// 初始化traceId
String traceId = MdcUtil.generateAndPutTraceId();
String param = XxlJobHelper.getJobParam();
if(StringUtils.isBlank(param)){
log.warn("lyricTodoListHandler param null");
log.warn("{} lyricTodoListHandler param null",traceId);
return;
}
XxlJobHelper.log("XXL-JOB:拉起lyric代办任务开始,param:{}", param);
XxlJobHelper.log("{} XXL-JOB:拉起lyric代办任务开始,param:{}", traceId,param);
JSONObject paramJson = JSONObject.parseObject(param);
ThreadLocalContext.setTenantId(paramJson.getLong("tenantId"));
ThreadLocalContext.setUserId(paramJson.getLong("userId"));
long startTime = System.currentTimeMillis();
SdmResponse response = lyricInternalService.getTodoList();
long endTime = System.currentTimeMillis();
log.info("lyricTodoListHandler cost [{}] s", (endTime - startTime)/1000);
log.info("lyricTodoListHandler result :{}", JSON.toJSONString(response));
XxlJobHelper.log("XXL-JOB:拉起lyric代办任务结束:{}", JSON.toJSONString(response));
long second = (endTime - startTime) / 1000;
log.info("{} lyricTodoListHandler cost [{}] s", traceId,second);
log.info("{} lyricTodoListHandler result :{}", traceId,JSON.toJSONString(response));
XxlJobHelper.log("{} XXL-JOB:拉起lyric代办任务结束:{},耗时:{} s", traceId,JSON.toJSONString(response),second);
// 销毁
MdcUtil.removeTraceId();
}
}

View File

@@ -1,44 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 彩色日志 -->
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr([%X{traceId}] %d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr([%15.15t]){faint} %clr(%logger){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}" />
<!-- 普通日志格式(无颜色) -->
<property name="FILE_LOG_PATTERN" value="[%X{traceId}] %d{yyyy-MM-dd HH:mm:ss.SSS} %5p ${PID:- } [%15.15t] %logger : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}" />
<!--定义日志文件存储地址 勿在 LogBack 的配置中使用相对路径-->
<!-- 日志文件存储地址 -->
<property name="LOG_HOME" value="/home/app/project/logs" />
<!-- 控制台输出 -->
<!-- 1. 控制台输出默认输出DEBUG及以上级别 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期%thread表示线程名%-5level级别从左显示5个字符宽度%msg日志消息%n是换行符-->
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 按照每天生成日志文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${LOG_HOME}/console.log.%d{yyyy-MM-dd}.log</FileNamePattern>
<!--日志文件保留天数-->
<!-- 2. INFO级别日志输出到running.log仅包含INFO及以上不包含DEBUG -->
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/running.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<FileNamePattern>${LOG_HOME}/running.log.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
<MaxHistory>30</MaxHistory>
<TotalSizeCap>500MB</TotalSizeCap>
<cleanHistoryOnStart>true</cleanHistoryOnStart>
<maxFileSize>10MB</maxFileSize>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期%thread表示线程名%-5level级别从左显示5个字符宽度%msg日志消息%n是换行符-->
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
<!--日志文件最大的大小-->
<!-- <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">-->
<!-- <MaxFileSize>10MB</MaxFileSize>-->
<!-- </triggeringPolicy>-->
<!-- 过滤只接受INFO及以上级别INFO, WARN, ERROR排除DEBUG -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
</appender>
<!-- 3. DEBUG级别日志输出到running_debug.log仅包含DEBUG级别 -->
<appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/running_debug.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<FileNamePattern>${LOG_HOME}/running_debug.log.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
<MaxHistory>30</MaxHistory>
<TotalSizeCap>500MB</TotalSizeCap>
<maxFileSize>10MB</maxFileSize>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
<!-- 过滤只接受DEBUG级别 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch> <!-- 匹配DEBUG级别则接受 -->
<onMismatch>DENY</onMismatch> <!-- 不匹配则拒绝 -->
</filter>
</appender>
<!-- 4. core.log 专用输出器(保留 callerInfo 格式) -->
@@ -59,15 +76,17 @@
</filter>
</appender>
<!-- 日志输出级别 -->
<!-- 全局日志级别设置为DEBUG确保DEBUG日志能被捕获 -->
<root level="INFO">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
<appender-ref ref="STDOUT" /> <!-- 控制台输出DEBUG及以上 -->
<appender-ref ref="INFO_FILE" /> <!-- INFO及以上输出到running.log -->
<appender-ref ref="DEBUG_FILE" /> <!-- 仅DEBUG输出到running_debug.log -->
</root>
<!-- 绑定 FeignClient → 输出到日志文件 -->
<logger name="FeignClient" level="INFO" additivity="false">
<appender-ref ref="FILE" />
<appender-ref ref="INFO_FILE" />
<appender-ref ref="STDOUT" />
</logger>
@@ -111,6 +130,7 @@
<logger name="java.sql.ResultSet" level="DEBUG"/>
<logger name="com.sdm.project" level="INFO"/>
<logger name="com.sdm.project.dao" level="DEBUG"/>
<!-- 新增com.sdm.outbridge 包日志级别配置 -->
<logger name="com.sdm.outbridge" level="INFO"/>
<logger name="com.sdm.project.dao" level="DEBUG"/>