首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >Spring Boot -如何在单一位置记录所有异常的请求和响应?

Spring Boot -如何在单一位置记录所有异常的请求和响应?
EN

Stack Overflow用户
提问于 2015-11-17 05:21:25
回答 14查看 449.2K关注 0票数 299

我正在用spring boot开发rest api。我需要记录所有的请求与输入参数(与方法,例如。GET、POST等)、请求路径、查询字符串、此请求对应的类方法、以及此操作的响应,包括成功和错误。例如:

请求成功:

代码语言:javascript
复制
http://example.com/api/users/1

日志应如下所示:

代码语言:javascript
复制
{
   HttpStatus: 200,
   path: "api/users/1",
   method: "GET",
   clientIp: "0.0.0.0",
   accessToken: "XHGu6as5dajshdgau6i6asdjhgjhg",
   method: "UsersController.getUser",
   arguments: {
     id: 1 
   },
   response: {
      user: {
        id: 1,
        username: "user123",
        email: "user123@example.com"   
      }
   },
   exceptions: []       
}

或者请求有错误:

代码语言:javascript
复制
http://example.com/api/users/9999

Log应该是这样的:

代码语言:javascript
复制
{
   HttpStatus: 404,
   errorCode: 101,                 
   path: "api/users/9999",
   method: "GET",
   clientIp: "0.0.0.0",
   accessToken: "XHGu6as5dajshdgau6i6asdjhgjhg",
   method: "UsersController.getUser",
   arguments: {
     id: 9999 
   },
   returns: {            
   },
   exceptions: [
     {
       exception: "UserNotFoundException",
       message: "User with id 9999 not found",
       exceptionId: "adhaskldjaso98d7324kjh989",
       stacktrace: ...................    
   ]       
}

我希望请求/响应是一个单一的实体,在成功和错误的情况下,都有与这个实体相关的自定义信息。

在spring中实现这一点的最佳实践是什么,可能是使用过滤器?如果有,你能提供具体的例子吗?

我使用过@ControllerAdvice@ExceptionHandler,但正如我前面提到的,我需要在单一位置(和单一日志)处理所有成功和错误请求。

EN

回答 14

Stack Overflow用户

发布于 2017-04-01 17:32:13

Spring已经提供了一个过滤器来完成这项工作。将以下bean添加到配置中

代码语言:javascript
复制
@Bean
public CommonsRequestLoggingFilter requestLoggingFilter() {
    CommonsRequestLoggingFilter loggingFilter = new CommonsRequestLoggingFilter();
    loggingFilter.setIncludeClientInfo(true);
    loggingFilter.setIncludeQueryString(true);
    loggingFilter.setIncludePayload(true);
    loggingFilter.setMaxPayloadLength(64000);
    return loggingFilter;
}

不要忘记将org.springframework.web.filter.CommonsRequestLoggingFilter的日志级别更改为DEBUG

票数 140
EN

Stack Overflow用户

发布于 2017-07-11 17:59:52

我在application.properties中定义了日志记录级别,以打印日志文件中的请求/响应、方法url

代码语言:javascript
复制
logging.level.org.springframework.web=DEBUG
logging.level.org.hibernate.SQL=INFO
logging.file=D:/log/myapp.log

我用的是Spring Boot。

票数 46
EN

Stack Overflow用户

发布于 2017-02-03 19:37:21

下面是我如何在spring data rest中使用org.springframework.web.util.ContentCachingRequestWrapperorg.springframework.web.util.ContentCachingResponseWrapper完成此操作

代码语言:javascript
复制
/**
 * Doogies very cool HTTP request logging
 *
 * There is also {@link org.springframework.web.filter.CommonsRequestLoggingFilter}  but it cannot log request method
 * And it cannot easily be extended.
 *
 * https://mdeinum.wordpress.com/2015/07/01/spring-framework-hidden-gems/
 * http://stackoverflow.com/questions/8933054/how-to-read-and-copy-the-http-servlet-response-output-stream-content-for-logging
 */
public class DoogiesRequestLogger extends OncePerRequestFilter {

  private boolean includeResponsePayload = true;
  private int maxPayloadLength = 1000;

  private String getContentAsString(byte[] buf, int maxLength, String charsetName) {
    if (buf == null || buf.length == 0) return "";
    int length = Math.min(buf.length, this.maxPayloadLength);
    try {
      return new String(buf, 0, length, charsetName);
    } catch (UnsupportedEncodingException ex) {
      return "Unsupported Encoding";
    }
  }

  /**
   * Log each request and respponse with full Request URI, content payload and duration of the request in ms.
   * @param request the request
   * @param response the response
   * @param filterChain chain of filters
   * @throws ServletException
   * @throws IOException
   */
  @Override
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

    long startTime = System.currentTimeMillis();
    StringBuffer reqInfo = new StringBuffer()
     .append("[")
     .append(startTime % 10000)  // request ID
     .append("] ")
     .append(request.getMethod())
     .append(" ")
     .append(request.getRequestURL());

    String queryString = request.getQueryString();
    if (queryString != null) {
      reqInfo.append("?").append(queryString);
    }

    if (request.getAuthType() != null) {
      reqInfo.append(", authType=")
        .append(request.getAuthType());
    }
    if (request.getUserPrincipal() != null) {
      reqInfo.append(", principalName=")
        .append(request.getUserPrincipal().getName());
    }

    this.logger.debug("=> " + reqInfo);

    // ========= Log request and response payload ("body") ========
    // We CANNOT simply read the request payload here, because then the InputStream would be consumed and cannot be read again by the actual processing/server.
    //    String reqBody = DoogiesUtil._stream2String(request.getInputStream());   // THIS WOULD NOT WORK!
    // So we need to apply some stronger magic here :-)
    ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request);
    ContentCachingResponseWrapper wrappedResponse = new ContentCachingResponseWrapper(response);

    filterChain.doFilter(wrappedRequest, wrappedResponse);     // ======== This performs the actual request!
    long duration = System.currentTimeMillis() - startTime;

    // I can only log the request's body AFTER the request has been made and ContentCachingRequestWrapper did its work.
    String requestBody = this.getContentAsString(wrappedRequest.getContentAsByteArray(), this.maxPayloadLength, request.getCharacterEncoding());
    if (requestBody.length() > 0) {
      this.logger.debug("   Request body:\n" +requestBody);
    }

    this.logger.debug("<= " + reqInfo + ": returned status=" + response.getStatus() + " in "+duration + "ms");
    if (includeResponsePayload) {
      byte[] buf = wrappedResponse.getContentAsByteArray();
      this.logger.debug("   Response body:\n"+getContentAsString(buf, this.maxPayloadLength, response.getCharacterEncoding()));
    }

    wrappedResponse.copyBodyToResponse();  // IMPORTANT: copy content of response back into original response

  }


}
票数 28
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/33744875

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档