如何使用Spring MVC正确记录http请求

Lon*_*don 38 java logging spring spring-mvc

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

@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;
    }
Run Code Online (Sandbox Code Playgroud)

所以我现在对不同的控制器/方法所做的就是从方法内部的开头复制所有内容,直到处理逻辑从方法到方法不同,然后复制下面的所有内容,如上面模板所示.

它有点乱,并且有很多代码重复(我不喜欢).但我需要记录所有内容.

有没有人对这种日志记录有更多的经验,谁能对此有所了解?

Dav*_*mes 44

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

Spring支持这一点.请参阅CommonsRequestLoggingFilter.如果使用Spring Boot,只需注册该类型的bean,Boot就会将其应用于过滤器链.喜欢:

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

此外,此日志记录筛选器要求将日志级别设置为DEBUG.例如,在logback.xml中执行以下操作:

<logger name="org.springframework.web.filter.CommonsRequestLoggingFilter" level="DEBUG"/>
Run Code Online (Sandbox Code Playgroud)

  • 你也可以覆盖:'​​`CommonsRequestLoggingFilter filter = new CommonsRequestLoggingFilter(){protected boolean shouldLog(javax.servlet.http.HttpServletRequest request){return true;}; };` (4认同)

Boz*_*zho 26

使用拦截器:

  • 扩展HandlerInterceptorAdapter和覆盖preHandle
  • <mvc:interceptors>in 定义它dispatcher-servlet.xml

它将针对每个请求运行.


小智 7

读取请求的主要问题是,只要输入流被消耗,它就会消失...而且无法再次读取.因此必须缓存输入流.Spring没有为缓存编写自己的类(可以在Web上的几个地方找到),而是提供了一些有用的类,即ContentCachingRequestWrapperContentCachingResponseWrapper.可以非常有效地利用这些类,例如,用于记录目的的过滤器.

在web.xml中定义过滤器:

<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>
Run Code Online (Sandbox Code Playgroud)

由于过滤器声明为DelegatingFilterProxy,因此可以使用@Component或@Bean注释将其声明为bean.在loggingFilter的doFilter方法中,在将请求和响应传递给过滤器链之前,使用spring提供的类包装请求和响应:

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

一旦输入流在chain.doFilter()之后被使用,输入流将被缓存在包装的请求中.然后可以访问如下:

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;
}
Run Code Online (Sandbox Code Playgroud)

但是,响应的情况有点不同.由于响应在传递给过滤器链之前也被包装,因此一旦在返回的路上写入,它也将被缓存到输出流.但由于输出流也将被消耗,因此您必须使用wrapper.copyBodyToResponse()将响应复制回输出流.见下文:

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;
}
Run Code Online (Sandbox Code Playgroud)

希望能帮助到你!


Isr*_*nov 6

这是我写的一个小库,你可以使用:spring-mvc-logger

我通过maven central提供它:

<dependency>
    <groupId>com.github.isrsal</groupId>
    <artifactId>spring-mvc-logger</artifactId>
    <version>0.2</version>
</dependency>
Run Code Online (Sandbox Code Playgroud)

  • 那么异步响应(servlet 3)呢?它似乎不起作用. (2认同)