如何使用Spring MVC正确logginghttp请求

你好我一直在试图找出通用的方式来logging我的应用程序中的http请求,到目前为止没有运气,这里是我如何处理现在的日志logging,即:

@RequestMapping(value="register", method = RequestMethod.POST) @ResponseBody public String register(@RequestParam(value="param1",required=false) String param1, @RequestParam("param2") String param2, @RequestParam("param3") String param3, HttpServletRequest request){ long start = System.currentTimeMillis(); logger.info("!--REQUEST START--!"); logger.info("Request URL: " + request.getRequestURL().toString()); List<String> requestParameterNames = Collections.list((Enumeration<String>)request.getParameterNames()); logger.info("Parameter number: " + requestParameterNames.size()); for (String parameterName : requestParameterNames){ logger.info("Parameter name: " + parameterName + " - Parameter value: " + request.getParameter(parameterName)); } //Some processing logic, call to the various services/methods with different parameters, response is always String(Json) String response = service.callSomeServiceMethods(param1,param2,param3); logger.info("Response is: " + response); long end = System.currentTimeMillis(); logger.info("Requested completed in: " + (end-start) + "ms"); logger.info("!--REQUEST END--!"); return response; } 

所以我现在对不同的控制器/方法所做的事情就是复制从方法内部开始直到不同方法之间的处理逻辑,然后复制上面模板中显示的所有内容。

这是一种混乱,有很多代码重复(我不喜欢)。 但我需要logging一切。

有没有人有这种日志logging更多的经验,任何人都可以阐明这一点?

使用拦截器 :

  • 扩展HandlerInterceptorAdapter并覆盖preHandle
  • dispatcher-servlet.xml中用<mvc:interceptors>来定义它

它将运行每个请求。

编辑:另外,请参阅@ membersound对此答案的评论,这改善了这个答案。

Spring支持这个。 请参阅CommonsRequestLoggingFilter 。 如果使用Spring Boot,只需注册一个该types的Bean,Boot就会将其应用到filter链中。 喜欢:

 @Bean public Filter logFilter() { CommonsRequestLoggingFilter filter = new CommonsRequestLoggingFilter(); filter.setIncludeQueryString(true); filter.setIncludePayload(true); filter.setMaxPayloadLength(5120); return filter; } 

此外,此日志logging筛选器要求将日志级别设置为DEBUG。 例如,在一个logback.xml文件中使用:

 <logger name="org.springframework.web.filter.CommonsRequestLoggingFilter" level="DEBUG"/> 

读取请求的主要问题是,只要inputstream被消耗掉了,不能被再次读取。 所以inputstream必须被caching。 Spring没有提供自己的caching类(可以在网上的几个地方find),而是提供了一些有用的类,比如ContentCachingRequestWrapper和ContentCachingResponseWrapper 。 这些类可以被非常有效地利用,例如,用于logging目的的filter。

在web.xml中定义一个filter:

 <filter> <filter-name>loggingFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>loggingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> 

由于filter声明为DelegatingFilterProxy,因此可以使用@Component或@Bean注释将其声明为一个bean。 在loggingFilter的doFilter方法中,在将请求和响应传递给filter链之前,用Spring提供的类封装请求和响应:

 HttpServletRequest requestToCache = new ContentCachingRequestWrapper(request); HttpServletResponse responseToCache = new ContentCachingResponseWrapper(response); chain.doFilter(requestToCache, responseToCache); String requestData = getRequestData(requestToCache); String responseData = getResponseData(responseToCache); 

一旦inputstream在chain.doFilter()之后被消耗,inputstream将被caching在被包装的请求中。 那么它可以被访问如下:

 public static String getRequestData(final HttpServletRequest request) throws UnsupportedEncodingException { String payload = null; ContentCachingRequestWrapper wrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class); if (wrapper != null) { byte[] buf = wrapper.getContentAsByteArray(); if (buf.length > 0) { payload = new String(buf, 0, buf.length, wrapper.getCharacterEncoding()); } } return payload; } 

不过,对于回应,情况有点不同。 由于在将响应传递给filter链之前,响应也被封装了,所以一旦它被写回去,它也会被caching到输出stream中。 但是由于输出stream也将被消耗,因此您必须使用wrapper.copyBodyToResponse()将响应复制回输出stream。 见下文:

 public static String getResponseData(final HttpServletResponse response) throws IOException { String payload = null; ContentCachingResponseWrapper wrapper = WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class); if (wrapper != null) { byte[] buf = wrapper.getContentAsByteArray(); if (buf.length > 0) { payload = new String(buf, 0, buf.length, wrapper.getCharacterEncoding()); wrapper.copyBodyToResponse(); } } return payload; } 

希望能帮助到你!

下面是我写的一个小型库,可以使用: spring-mvc-logger

我通过maven中心提供:

 <dependency> <groupId>com.github.isrsal</groupId> <artifactId>spring-mvc-logger</artifactId> <version>0.2</version> </dependency> 

作为任何技术的答案…这取决于你正在使用的技术堆栈和你的要求是什么。

例如,你想使你的日志更通用,更进一步,你会想要做到这一点。 在你的情况下,你只logging日志启用和在spring的上下文处理的请求。 所以你可能会“错过”其他请求。

我会看看你用来运行你的应用程序的容器或Web服务器。 这将消除对Spring的依赖。 Plus容器为您提供了插入日志logging提供程序的灵活性,然后configuration日志外部代码的格式。 例如,如果您使用的是Apache Web服务器,请使用Apache Web服务器日志logging来logging访问日志logging层中的所有HTTP请求。 但要小心,一些日志选项有性能处罚。 只logging您对访问模式监控视angular的严重需求。

如果你使用的是tomcat,那么tomcat也会允许你logging东西。 在你正在使用的tomcat的tomcat文档中searchAccess Valve。 这将打开一个可能性的世界。

更广泛的日志logging应该是exception策略的领域,即在系统出现问题时想要看到的细节types。

添加到@ B.Ali已经回答了什么。 如果你在一个spring的asynchronous请求(serlvet 3.0或更高版本)处理场景中使用这个,那么下面的代码就是为我工作的。

 public class OncePerRequestLoggingFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { boolean isFirstRequest = !isAsyncDispatch(request); HttpServletRequest requestToUse = request; HttpServletResponse responseToUse = response; // The below check is critical and if not there, then the request/response gets corrupted. // Probably because in async case the filter is invoked multiple times. if (isFirstRequest && !(request instanceof ContentCachingRequestWrapper)) { requestToUse = new ContentCachingRequestWrapper(request); } if (isFirstRequest && !(response instanceof ContentCachingResponseWrapper)) { responseToUse = new ContentCachingResponseWrapper(response); } filterChain.doFilter(requestToUse, responseToUse); if (!isAsyncStarted(request)) { ContentCachingResponseWrapper responseWrapper = WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class); responseWrapper.copyBodyToResponse(); // IMPORTANT to copy it back to response } } @Override protected boolean shouldNotFilterAsyncDispatch() { return false; // IMPORTANT this is true by default and wont work in async scenario. } 

}