//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<String> EXCLUDE_PATHS = new HashSet<>(Arrays.asList(
|
// "/actuator", "/healthcheck", "/health", "/prometheus"
|
// ));
|
//
|
// /** 只对这些 Content-Type 打印 body(避免打印二进制流) */
|
// private static final Set<String> 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<String> 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<String> 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);
|
// }
|
//}
|