上面要求添加 traceId,需求很简单。
唯一的难点是,jakarta.servlet.http.HttpServletRequest 不支持直接 put 请求头。
所以需要创建一个可修改的对象,包装请求。
另外,由于我们应用还使用了 openFeign ,需要将 traceId 传递进去。
PS: 老大非要把appName用做traceId前缀,无语~
过滤器
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;/*** 追踪id过滤器**/
@WebFilter(urlPatterns = "/*")
@Component
@Slf4j
public class TraceIdFilter extends HttpFilter {@Value("${spring.application.name:appName}")private String appName;@Overridepublic void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) {try {// 添加 HeaderString traceId = request.getHeader(HConst.TRACE_ID);// 已存在则放行if (StringUtils.isNotBlank(traceId)) {// 放入日志MDC.put(HConst.TRACE_ID, traceId);response.setHeader(HConst.TRACE_ID,traceId);chain.doFilter(request, response);} else {// 不存在则自生成traceId = appName + "-" + UUIDGenerator.getUUID();log.info("appGenerator traceId : {}",traceId);// 推入请求MutableHttpServletRequest muRequest = new MutableHttpServletRequest(request);response.setHeader(HConst.TRACE_ID,traceId);muRequest.putHeader(HConst.TRACE_ID, traceId);// 放入日志MDC.put(HConst.TRACE_ID, traceId);chain.doFilter(muRequest, response);}} catch (Exception e) {log.error("TRACE_id异常:", e);throw new RuntimeException("TRACE_id异常");}}
}
可修改的请求适配器
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;import java.util.*;/*** 可修改的请求对象**/
public final class MutableHttpServletRequest extends HttpServletRequestWrapper {/*** 保持自定义头、值*/private final Map<String, String> customHeaders;public MutableHttpServletRequest(HttpServletRequest request){super(request);this.customHeaders = new HashMap<>();}public void putHeader(String name, String value){this.customHeaders.put(name, value);}@Overridepublic String getHeader(String name) {// 检查String headerValue = customHeaders.get(name);if (headerValue != null){return headerValue;}// 返回原头return ((HttpServletRequest) getRequest()).getHeader(name);}@Overridepublic Enumeration<String> getHeaderNames() {// 创建自定义头容器Set<String> set = new HashSet<String>(customHeaders.keySet());// 添加头@SuppressWarnings("unchecked")Enumeration<String> e = ((HttpServletRequest) getRequest()).getHeaderNames();while (e.hasMoreElements()) {// 复制到新容器String n = e.nextElement();set.add(n);}// 重新构建并返回return Collections.enumeration(set);}}
调用了 openFeign,将 traceId 传入代码
@Slf4j
@Component
public class CipherInterceptor implements RequestInterceptor {@Overridepublic void apply(RequestTemplate template) {try {// 添加traceIdaddTraceId(template);byte[] body = template.body();String bodyStr = new String(body, StandardCharsets.UTF_8);// 打印请求消息log.info("feign-request-path: {}", template.path());log.info("feign-request-bodyStr : {}", bodyStr);} catch (Exception e) {log.error("CipherInterceptor error :", e);}}/*** 添加请求头* @param template 请求对象*/private static void addTraceId(RequestTemplate template) {Map<String, Collection<String>> headers = template.headers();String traceId = ServletUtil.getHeader(HConst.TRACE_ID);if (StringUtils.isBlank(traceId)) {traceId = "feign-" + UUIDGenerator.getUUID();}headers.put(HConst.TRACE_ID, Collections.singletonList(traceId));log.info("feign-request-headers: {}", headers);}}
其它代码
从请求上下文中获取请求头方法,生成 UUID
public static String getHeader(String name){// 获取请求头信息ServletRequestAttributes requestAttributes =(ServletRequestAttributes)RequestContextHolder.getRequestAttributes();HttpServletRequest request = null;if (requestAttributes != null) {request = requestAttributes.getRequest();return request.getHeader(name);}return null;}public static String getUUID() {String s = UUID.randomUUID().toString();return s.replaceAll("-", "");}