Spring Boot+GraphQL服务中Logback Access无法捕获响应内容排查
问题:Spring Boot + Spring for GraphQL中Logback Access无法捕获响应内容
我们正在使用Spring Boot和Spring for GraphQL开发新服务,希望启用Logback Access日志以捕获请求和响应的头信息及内容。已完成相关配置,但目前仅能捕获请求头与请求内容,响应内容始终为空。
已配置信息
application.yaml
logback: access: enabled: true config: classpath:logback/logback-access.xml useServerPortInsteadOfLocalPort: true tee-filter: enabled: true tomcat: enableRequestAttributes: true
logback-access.xml
<configuration> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%fullRequest%n%n%fullResponse</pattern> </encoder> </appender> <appender-ref ref="CONSOLE"/> </configuration>
TeeFilter配置(已尝试order取值[-1,0,1,9999,-9999])
@Bean public FilterRegistrationBean<TeeFilter> someFilterRegistration() { FilterRegistrationBean<TeeFilter> registrationBean = new FilterRegistrationBean<>(new TeeFilter()); registrationBean.addUrlPatterns("/*"); registrationBean.setName("TeeFilter"); registrationBean.setOrder(-1); return registrationBean; }
控制台输出示例显示请求内容正常,但响应无内容。此配置在其他Spring Boot REST服务中可正常捕获响应内容,请问该问题是否与Spring for GraphQL或Graphql for Java相关?
解决方案
这个问题确实和Spring for GraphQL的响应处理机制直接相关:
- 普通Spring Boot REST服务的响应会经过Spring MVC完整的Filter链处理,TeeFilter能在响应流提交前捕获内容;
- 而Spring for GraphQL的
GraphQlHttpHandler是直接向Servlet响应流写入内容,绕过了Filter链中后续的响应拦截逻辑,导致TeeFilter无法获取到响应数据。
可以通过以下两种方式解决:
方式一:使用ContentCachingResponseWrapper包装响应
自定义Filter在请求进入GraphQL处理器前包装响应,确保响应内容被缓存,让TeeFilter能捕获到:
- 实现自定义Filter:
import org.springframework.web.filter.OncePerRequestFilter; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.web.util.ContentCachingResponseWrapper; import java.io.IOException; public class ResponseCachingFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { ContentCachingResponseWrapper wrappedResponse = new ContentCachingResponseWrapper(response); try { filterChain.doFilter(request, wrappedResponse); } finally { wrappedResponse.copyBodyToResponse(); } } }
- 注册该Filter,设置比TeeFilter更高的优先级:
@Bean public FilterRegistrationBean<ResponseCachingFilter> responseCachingFilterRegistration() { FilterRegistrationBean<ResponseCachingFilter> registrationBean = new FilterRegistrationBean<>(new ResponseCachingFilter()); registrationBean.addUrlPatterns("/*"); registrationBean.setName("ResponseCachingFilter"); registrationBean.setOrder(-2); // 优先级高于TeeFilter的-1 return registrationBean; }
方式二:使用Spring for GraphQL拦截器记录响应
直接通过GraphQL原生拦截器捕获响应内容,配合Logback日志输出:
- 实现WebGraphQlInterceptor:
import org.springframework.graphql.server.WebGraphQlInterceptor; import org.springframework.graphql.server.WebGraphQlRequest; import org.springframework.graphql.server.WebGraphQlResponse; import reactor.core.publisher.Mono; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class GraphQlResponseLoggingInterceptor implements WebGraphQlInterceptor { private static final Logger logger = LoggerFactory.getLogger(GraphQlResponseLoggingInterceptor.class); @Override public Mono<WebGraphQlResponse> intercept(WebGraphQlRequest request, Chain chain) { return chain.next(request) .doOnNext(response -> { String responseContent = response.toExecutionResult().toString(); logger.info("GraphQL Response: {}", responseContent); }); } }
- 注册拦截器:
@Bean public WebGraphQlInterceptor graphQlResponseLoggingInterceptor() { return new GraphQlResponseLoggingInterceptor(); }
这种方式可以更精准地控制GraphQL请求的日志记录,无需依赖Logback Access的TeeFilter。
内容的提问来源于stack exchange,提问作者Nodirbek Shamsiev
相关产品推荐
相关产品推荐

