//package com.xiaopeng.trafficControl.config.log; // //import com.xiaopeng.common.log.config.LogHeaderProperties; //import lombok.extern.slf4j.Slf4j; //import org.slf4j.MDC; //import org.springframework.beans.factory.annotation.Autowired; //import org.springframework.core.Ordered; //import org.springframework.core.annotation.Order; //import org.springframework.http.MediaType; //import org.springframework.stereotype.Component; //import org.springframework.web.filter.OncePerRequestFilter; //import org.springframework.web.util.ContentCachingRequestWrapper; //import org.springframework.web.util.ContentCachingResponseWrapper; // //import javax.servlet.FilterChain; //import javax.servlet.ServletException; //import javax.servlet.http.HttpServletRequest; //import javax.servlet.http.HttpServletResponse; //import java.io.IOException; //import java.nio.charset.StandardCharsets; //import java.util.*; // ///** // * 请求/响应日志过滤器 // * 打印:traceId、method、uri、queryString、请求体、响应状态、耗时、响应体 // */ //@Slf4j //@Component //@Order(Ordered.HIGHEST_PRECEDENCE + 10) //public class RequestResponseLoggingFilter extends OncePerRequestFilter { // // @Autowired // private LogHeaderProperties logHeaderProperties; // // /** 不打印 body 的路径前缀(健康检查、actuator 等) */ // private static final Set EXCLUDE_PATHS = new HashSet<>(Arrays.asList( // "/actuator", "/healthcheck", "/health", "/prometheus" // )); // // /** 只对这些 Content-Type 打印 body(避免打印二进制流) */ // private static final Set LOGGABLE_CONTENT_TYPES = new HashSet<>(Arrays.asList( // MediaType.APPLICATION_JSON_VALUE, // MediaType.APPLICATION_JSON_UTF8_VALUE, // MediaType.TEXT_PLAIN_VALUE, // MediaType.TEXT_XML_VALUE, // MediaType.APPLICATION_XML_VALUE, // "application/x-www-form-urlencoded" // )); // // private static final int MAX_BODY_LENGTH = 2000; // // @Override // protected boolean shouldNotFilter(HttpServletRequest request) { // String uri = request.getRequestURI(); // return EXCLUDE_PATHS.stream().anyMatch(uri::startsWith); // } // // @Override // protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, // FilterChain chain) throws ServletException, IOException { // // 初始化 MDC(Filter 先于 Interceptor 执行,在此统一设置) // String traceId = request.getHeader("X-Trace-Id"); // if (traceId == null || traceId.isEmpty()) { // traceId = UUID.randomUUID().toString().replace("-", ""); // } // MDC.put("traceId", traceId); // MDC.put("clientIp", getClientIp(request)); // MDC.put("requestUri", request.getRequestURI()); // response.setHeader("X-Trace-Id", traceId); // // ContentCachingRequestWrapper reqWrapper = new ContentCachingRequestWrapper(request); // ContentCachingResponseWrapper respWrapper = new ContentCachingResponseWrapper(response); // // long start = System.currentTimeMillis(); // try { // chain.doFilter(reqWrapper, respWrapper); // } finally { // long cost = System.currentTimeMillis() - start; // logRequest(reqWrapper); // logResponse(respWrapper, cost); // // 必须把响应体写回,否则客户端收不到数据 // respWrapper.copyBodyToResponse(); // MDC.clear(); // } // } // // private String getClientIp(HttpServletRequest request) { // String ip = request.getHeader("X-Forwarded-For"); // if (ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip)) { // return ip.split(",")[0].trim(); // } // ip = request.getHeader("X-Real-IP"); // if (ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip)) { // return ip; // } // return request.getRemoteAddr(); // } // // private void logRequest(ContentCachingRequestWrapper request) { // String traceId = MDC.get("traceId"); // String uri = request.getRequestURI(); // String query = request.getQueryString(); // String method = request.getMethod(); // String body = extractBody(request.getContentAsByteArray(), request.getContentType()); // // if (logHeaderProperties.isEnabled()) { // String headers = extractHeaders(request); // log.info(">>> [{}] {} {}{} | headers={} | body={}", // traceId, method, uri, // query != null ? "?" + query : "", // headers, body); // } else { // log.info(">>> [{}] {} {}{} | body={}", // traceId, method, uri, // query != null ? "?" + query : "", // body); // } // } // // private String extractHeaders(HttpServletRequest request) { // List names = logHeaderProperties.getNames(); // StringBuilder sb = new StringBuilder("{"); // boolean first = true; // if (names != null && !names.isEmpty()) { // // 只打印配置的请求头 // for (String name : names) { // String value = request.getHeader(name); // if (value != null) { // if (!first) sb.append(", "); // first = false; // sb.append(name).append("=").append(value); // } // } // } else { // // 未配置则打印全部请求头 // Enumeration headerNames = request.getHeaderNames(); // while (headerNames != null && headerNames.hasMoreElements()) { // String name = headerNames.nextElement(); // String value = request.getHeader(name); // if (!first) sb.append(", "); // first = false; // sb.append(name).append("=").append(value); // } // } // return sb.append("}").toString(); // } // // private void logResponse(ContentCachingResponseWrapper response, long cost) { // String traceId = MDC.get("traceId"); // int status = response.getStatus(); // String body = extractBody(response.getContentAsByteArray(), response.getContentType()); // // log.info("<<< [{}] status={} cost={}ms | body={}", // traceId, status, cost, body); // } // // private String extractBody(byte[] bodyBytes, String contentType) { // if (bodyBytes == null || bodyBytes.length == 0) { // return ""; // } // if (!isLoggableContentType(contentType)) { // return "[binary, " + bodyBytes.length + " bytes]"; // } // String body = new String(bodyBytes, StandardCharsets.UTF_8); // if (body.length() > MAX_BODY_LENGTH) { // body = body.substring(0, MAX_BODY_LENGTH) + "...(truncated)"; // } // return body; // } // // private boolean isLoggableContentType(String contentType) { // if (contentType == null) return true; // String ct = contentType.toLowerCase(); // return LOGGABLE_CONTENT_TYPES.stream().anyMatch(ct::contains); // } //}