我正在尝试记录(仅为了简单起见,现在是控制台写入)最终呈现的HTML将由HttpServletResponse返回。 (即正文)为此,我正在使用Spring MVC中的HandlerInterceptorAdapter,如下所示:
public class VxmlResponseInterceptor extends HandlerInterceptorAdapter {
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println(response.toString());
}
}
这可以按预期工作,我在控制台中看到HTTP响应头。我的问题是,是否有一种相对简单的方法可以将整个响应体(即最终呈现的HTML)记录到控制台,而不必使用PrintWriters,OutputStream等进行跳跃。
提前致谢。
答案 0 :(得分:21)
使用Servlet Filter
而不是Spring HandlerInterceptor
会更好,因为允许Filter
替换请求和/或响应对象,而您可以使用此机制用记录响应输出的包装器替换响应。
这将涉及编写HttpServletResponseWrapper
的子类,覆盖getOutputStream
(可能还有getWriter()
)。除了发送到其原始目标之外,这些方法还会返回OutputStream
/ PrintWriter
实现,这些实现会将响应流中的虹吸带入日志中。一种简单的方法是使用TeeOutputStream
中的Apache Commons IO,但实现起来并不难。
这是一个你可以做的事情的例子,利用Spring的GenericFilterBean
和DelegatingServletResponseStream
以及TeeOutputStream
来简化事情:
public class ResponseLoggingFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse responseWrapper = loggingResponseWrapper((HttpServletResponse) response);
filterChain.doFilter(request, responseWrapper);
}
private HttpServletResponse loggingResponseWrapper(HttpServletResponse response) {
return new HttpServletResponseWrapper(response) {
@Override
public ServletOutputStream getOutputStream() throws IOException {
return new DelegatingServletOutputStream(
new TeeOutputStream(super.getOutputStream(), loggingOutputStream())
);
}
};
}
private OutputStream loggingOutputStream() {
return System.out;
}
}
这会将所有内容记录到STDOUT。如果你想登录一个文件,它会变得更复杂,确保流关闭等等,但原则仍然是相同的。
答案 1 :(得分:9)
如果你正在使用(或考虑)logback作为你的日志记录框架,那么有一个很好的servlet过滤器就可以完成。查看documentation中的TeeFilter章节。
答案 2 :(得分:6)
我一直在寻找一种方法来记录完整的HTTP请求/响应一段时间,并发现它已在Tomcat 7 RequestDumperFilter中为我解决了。它的工作方式与Tomcat 7容器中的广告相同。如果你想在Jetty中使用它,这个类可以很好地独立工作,或者像我一样,复制并适应我环境的特定需求。
答案 3 :(得分:1)
我通过maven central制作了一个小型库spring-mvc-logger。
添加到pom.xml:
<dependency>
<groupId>com.github.isrsal</groupId>
<artifactId>spring-mvc-logger</artifactId>
<version>0.2</version>
</dependency>
添加到web.xml:
<filter>
<filter-name>loggingFilter</filter-name>
<filter-class>com.github.isrsal.logging.LoggingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>loggingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
添加到log4j.xml:
<logger name="com.github.isrsal.logging.LoggingFilter">
<level value="DEBUG"/>
</logger>
答案 4 :(得分:1)
下面粘贴的代码适用于我的测试,可以从我的github project下载,在基于生产项目的解决方案上应用解决方案后可以共享
@Configuration
public class LoggingFilter extends GenericFilterBean {
/**
* It's important that you actually register your filter this way rather then just annotating it
* as @Component as you need to be able to set for which "DispatcherType"s to enable the filter
* (see point *1*)
*
* @return
*/
@Bean
public FilterRegistrationBean<LoggingFilter> initFilter() {
FilterRegistrationBean<LoggingFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new LoggingFilter());
// *1* make sure you sett all dispatcher types if you want the filter to log upon
registrationBean.setDispatcherTypes(EnumSet.allOf(DispatcherType.class));
// *2* this should put your filter above any other filter
registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return registrationBean;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
ContentCachingRequestWrapper wreq =
new ContentCachingRequestWrapper(
(HttpServletRequest) request);
ContentCachingResponseWrapper wres =
new ContentCachingResponseWrapper(
(HttpServletResponse) response);
try {
// let it be ...
chain.doFilter(wreq, wres);
// makes sure that the input is read (e.g. in 404 it may not be)
while (wreq.getInputStream().read() >= 0);
System.out.printf("=== REQUEST%n%s%n=== end request%n",
new String(wreq.getContentAsByteArray()));
// Do whatever logging you wish here, in this case I'm writing request
// and response to system out which is probably not what you wish to do
System.out.printf("=== RESPONSE%n%s%n=== end response%n",
new String(wres.getContentAsByteArray()));
// this is specific of the "ContentCachingResponseWrapper" we are relying on,
// make sure you call it after you read the content from the response
wres.copyBodyToResponse();
// One more point, in case of redirect this will be called twice! beware to handle that
// somewhat
} catch (Throwable t) {
// Do whatever logging you whish here, too
// here you should also be logging the error!!!
throw t;
}
}
}