You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 22:05:24