Spring AOP是否是HTTP入站及出站请求横切日志的最简解决方案?
HTTP入站/出站请求无侵入日志实现方案
除了AOP之外,还有以下几种无侵入式的HTTP请求日志实现方案,覆盖不同场景:
1. Spring MVC/WebFlux 全局拦截器
入站请求日志(Controller端点)
对于Spring MVC,实现HandlerInterceptor接口,在请求到达Controller前后记录日志:
@Component public class HttpRequestLogInterceptor implements HandlerInterceptor { private static final Logger logger = LoggerFactory.getLogger(HttpRequestLogInterceptor.class); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { logger.info("Inbound Request: {} {} | Params: {}", request.getMethod(), request.getRequestURI(), request.getQueryString()); return true; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { logger.info("Inbound Response: {} | Status: {}", request.getRequestURI(), response.getStatus()); } }
注册拦截器:
@Configuration public class WebConfig implements WebMvcConfigurer { @Autowired private HttpRequestLogInterceptor logInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(logInterceptor).addPathPatterns("/**"); } }
出站请求日志(RestTemplate/WebClient)
- 对于RestTemplate,添加
ClientHttpRequestInterceptor:
@Bean public RestTemplate restTemplate() { RestTemplate restTemplate = new RestTemplate(); restTemplate.getInterceptors().add((request, body, execution) -> { logger.info("Outbound Request: {} {} | Body: {}", request.getMethod(), request.getURI(), new String(body, StandardCharsets.UTF_8)); ClientHttpResponse response = execution.execute(request, body); logger.info("Outbound Response: {} | Status: {}", request.getURI(), response.getStatusCode()); return response; }); return restTemplate; }
- 对于WebClient(Reactive场景),使用
ExchangeFilterFunction:
@Bean public WebClient webClient() { return WebClient.builder() .filter((request, next) -> { logger.info("Reactive Outbound Request: {} {}", request.method(), request.url()); return next.exchange(request) .doOnSuccess(response -> logger.info("Reactive Outbound Response: {} | Status: {}", request.url(), response.statusCode())); }) .build(); }
2. Servlet 全局过滤器(Filter)
基于Servlet规范的过滤器,比Spring拦截器更底层,适配所有Servlet容器的应用,实现OncePerRequestFilter避免重复执行:
@Component public class GlobalHttpLogFilter extends OncePerRequestFilter { private static final Logger logger = LoggerFactory.getLogger(GlobalHttpLogFilter.class); @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 包装request以读取请求体(可选) ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request); ContentCachingResponseWrapper wrappedResponse = new ContentCachingResponseWrapper(response); try { filterChain.doFilter(wrappedRequest, wrappedResponse); } finally { // 记录请求 String requestBody = new String(wrappedRequest.getContentAsByteArray(), wrappedRequest.getCharacterEncoding()); logger.info("Global Inbound Request: {} {} | Body: {}", request.getMethod(), request.getRequestURI(), requestBody); // 记录响应 String responseBody = new String(wrappedResponse.getContentAsByteArray(), wrappedResponse.getCharacterEncoding()); logger.info("Global Inbound Response: {} | Status: {} | Body: {}", request.getRequestURI(), response.getStatus(), responseBody); // 必须调用此方法,否则响应体无法返回给客户端 wrappedResponse.copyBodyToResponse(); } } }
3. 网关层统一日志(微服务场景)
如果是微服务架构,可在网关(如Spring Cloud Gateway、Zuul)统一拦截所有入站/出站请求,业务服务无需任何改动:
以Spring Cloud Gateway的GlobalFilter为例:
@Component public class GatewayLogFilter implements GlobalFilter, Ordered { private static final Logger logger = LoggerFactory.getLogger(GatewayLogFilter.class); @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 记录入站请求 ServerHttpRequest request = exchange.getRequest(); logger.info("Gateway Inbound Request: {} {} | Params: {}", request.getMethod(), request.getPath(), request.getQueryParams()); // 记录出站响应 return chain.filter(exchange) .doOnSuccess(v -> { ServerHttpResponse response = exchange.getResponse(); logger.info("Gateway Outbound Response: {} | Status: {}", request.getPath(), response.getStatusCode()); }); } @Override public int getOrder() { return Ordered.LOWEST_PRECEDENCE; } }
关于Wiretap/全局通道拦截器的疑问
Wiretap是Spring Integration中用于监听消息通道的组件,主要针对消息驱动的场景(如消息队列、集成流),并不直接适配普通的HTTP Controller端点或RestClient请求。如果你的应用并非基于Spring Integration构建,它确实不是合适的选择。
内容的提问来源于stack exchange,提问作者al.truisme
相关产品推荐
相关产品推荐

