推荐用Filter而非Interceptor实现统一请求响应日志,因Filter能捕获完整请求体和响应体;需继承OncePerRequestFilter,包装请求响应对象并缓存内容,调用copyBodyToResponse()确保响应正常返回。

在 Spring Boot 中实现统一的请求响应日志拦截,推荐用 Filter 而非拦截器(Interceptor),因为 Filter 天然能捕获完整的请求体和响应体(包括 POST JSON、文件上传、流式响应等),而 Interceptor 无法直接读取已消费的 request.getInputStream 或 response.getOutputStream。
用 Filter 实现全链路日志记录
核心是继承 OncePerRequestFilter,包装原始请求和响应对象,确保可多次读取:
- 使用
ContentCachingRequestWrapper缓存请求内容,支持 GET 参数解析 + POST Body 读取 - 使用
ContentCachingResponseWrapper缓存响应内容,避免流被提前消费 - 在
doFilterInternal中执行链路调用后,再统一打印日志 - 务必调用
wrappedResponse.copyBodyToResponse(),否则前端收不到响应
关键代码示例
新建类 RequestResponseLogFilter:
public class RequestResponseLogFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request);
ContentCachingResponseWrapper wrappedResponse = new ContentCachingResponseWrapper(response);
<pre class="brush:php;toolbar:false;"> long startTime = System.currentTimeMillis();
try {
filterChain.doFilter(wrappedRequest, wrappedResponse);
} finally {
long cost = System.currentTimeMillis() - startTime;
logRequest(wrappedRequest, cost);
logResponse(wrappedResponse, cost);
wrappedResponse.copyBodyToResponse(); // 必须调用
}
}
private void logRequest(ContentCachingRequestWrapper request, long cost) {
String method = request.getMethod();
String uri = request.getRequestURI();
String params = getQueryString(request.getQueryString());
String body = getRequestBody(request);
log.info("[REQ {}ms] {} {} | Params: {} | Body: {}", cost, method, uri, params, body);
}
private void logResponse(ContentCachingResponseWrapper response, long cost) {
int status = response.getStatus();
String body = getResponseBody(response);
log.info("[RES {}ms] {} | Body: {}", cost, status, body);
}
}
注册 Filter 到容器
通过 @Bean 方式注入,无需额外配置 web.xml:
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<requestresponselogfilter> loggingFilter() {
FilterRegistrationBean<requestresponselogfilter> registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new RequestResponseLogFilter());
registrationBean.addUrlPatterns("/*"); // 拦截所有路径
registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE + 1); // 高优先级,早于其他 Filter
return registrationBean;
}
}</requestresponselogfilter></requestresponselogfilter>
日志输出与文件落盘
仅打印到控制台不够,生产环境必须写入文件。在 application.yml 中启用文件日志:
logging:
config: classpath:logback-spring.xml
file:
name: logs/app.log
level:
root: INFO
搭配标准 logback-spring.xml,设置按天滚动、保留7天、含时间线程级别等信息。这样即使服务器无法连 SSH,也能通过下载日志文件快速定位问题。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











