Skip to content

Latest commit

 

History

History
597 lines (466 loc) · 24.8 KB

31.spring_boot_2_interceptor_log.md

File metadata and controls

597 lines (466 loc) · 24.8 KB

Spring Boot 项目使用 HandlerInterceptor拦截器 以及 @ControllerAdvice实现日志记录



1 摘要

在 web 项目中,日志记录是必不可少的功能。数据分析与问题排查都少不了日志,其重要性不言而喻。在本项目中已经介绍过一种通过自行写 AOP 切点的方式实现日志记录。

10.springBoot2.0使用AOP打印controller层出入参日志.md

现在将再介绍一种通过实现 HandlerInterceptor 接口以及 @ControllerAdvice 注解来实现日志记录。

本文基于上一篇介绍拦截器的文章进行实现

30 Spring Boot 2(Spring 5.0+) HandlerInterceptor 拦截器简单示例

2 核心 Maven 依赖

./pom.xml
            <!-- web,mvc -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <version>${springboot.version}</version>
            </dependency>
./demo-common/pom.xml
        <!-- Servlet -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
        </dependency>
        <!-- Apache commons lang3 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
        <!-- web-mvc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
        </dependency>

其中 ${springboot.version} 版本为 2.0.6.RELEASE

3 核心代码

3.1 日志记录工具类

./demo-common/src/main/java/com/ljq/demo/springboot/common/log/LogService.java
package com.ljq.demo.springboot.common.log;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

/**
 * @Description: 日志记录工具类
 * @Author: junqiang.lu
 * @Date: 2020/1/8
 */
@Component
@Slf4j
public class LogService {


    /**
     * 记录请求参数日志
     *
     * @param request
     * @param body
     */
    public void logRequest(HttpServletRequest request, Object body) {
        log.info("[LOG-REQUEST]\n\trequestIP: {}\n\tcontentType:{}\n\trequestUrl: {}\n\t" +
                        "requestMethod: {}\n\trequestParams: {}\n\trequestBody: {}",
                getIpAddress(request),request.getHeader("Content-Type"),
                request.getRequestURL(), request.getMethod(), getRequestParamsMap(request), body);
    }

    /**
     * 记录返回参数日志
     *
     * @param request
     * @param response
     * @param body
     */
    public void logResponse(HttpServletRequest request, HttpServletResponse response, Object body) {
        log.info("[LOG-RESPONSE]\n\trequestIp: {}\n\trequestUrl: {}\n\tresponse: {}",
                getIpAddress(request), request.getRequestURL(), body);
    }

    /**
     * 获取请求参数
     *
     * @param httpServletRequest
     * @return
     */
    private Map<String, String> getRequestParamsMap(HttpServletRequest httpServletRequest) {
        Map<String, String> resultMap = new HashMap<>(16);
        if (httpServletRequest.getParameterMap() != null && !httpServletRequest.getParameterMap().isEmpty()) {
            for (Map.Entry<String, String[]> entry : httpServletRequest.getParameterMap().entrySet()) {
                resultMap.put(entry.getKey(),Arrays.toString(entry.getValue()));
            }
        }
        return resultMap;
    }

    /**
     * 获取客户端请求 ip
     *
     * @param request
     * @return
     */
    private static String getIpAddress(HttpServletRequest request) {
        String xip = request.getHeader("X-Real-IP");
        String xFor = request.getHeader("X-Forwarded-For");
        if(StringUtils.isNotEmpty(xFor) && !"unKnown".equalsIgnoreCase(xFor)){
            //多次反向代理后会有多个ip值,第一个ip才是真实ip
            int index = xFor.indexOf(",");
            if(index != -1){
                return xFor.substring(0,index);
            }else{
                return xFor;
            }
        }
        xFor = xip;
        if(StringUtils.isNotEmpty(xFor) && !"unKnown".equalsIgnoreCase(xFor)){
            return xFor;
        }
        if (StringUtils.isBlank(xFor) || "unknown".equalsIgnoreCase(xFor)) {
            xFor = request.getHeader("Proxy-Client-IP");
        }
        if (StringUtils.isBlank(xFor) || "unknown".equalsIgnoreCase(xFor)) {
            xFor = request.getHeader("WL-Proxy-Client-IP");
        }
        if (StringUtils.isBlank(xFor) || "unknown".equalsIgnoreCase(xFor)) {
            xFor = request.getHeader("HTTP_CLIENT_IP");
        }
        if (StringUtils.isBlank(xFor) || "unknown".equalsIgnoreCase(xFor)) {
            xFor = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (StringUtils.isBlank(xFor) || "unknown".equalsIgnoreCase(xFor)) {
            xFor = request.getRemoteAddr();
        }
        return xFor;
    }

}

3.2 HandlerInterceptor 拦截器

./demo-web/src/main/java/com/ljq/demo/springboot/web/acpect/SimpleInterceptor.java
package com.ljq.demo.springboot.web.acpect;

import com.ljq.demo.springboot.common.log.LogService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects;

/**
 * @Description: 简易拦截器应用
 * @Author: junqiang.lu
 * @Date: 2019/12/10
 */
@Slf4j
@Component
public class SimpleInterceptor implements HandlerInterceptor {

    @Autowired
    private LogService logService;


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("preHandle");
        if (Objects.nonNull(request.getQueryString())) {
            logService.logRequest(request, null);
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("postHandle");

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("afterCompletion");

    }
}

3.3 请求参数处理类

./demo-web/src/main/java/com/ljq/demo/springboot/web/acpect/CustomRequestBodyAdviceAdapter.java
package com.ljq.demo.springboot.web.acpect;

import com.ljq.demo.springboot.common.log.LogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdviceAdapter;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Type;

/**
 * @Description: 请求参数处理类
 * @Author: junqiang.lu
 * @Date: 2020/1/8
 */
@ControllerAdvice
public class CustomRequestBodyAdviceAdapter extends RequestBodyAdviceAdapter {

    @Autowired
    private LogService logService;
    @Autowired
    HttpServletRequest request;

    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    @Override
    public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        logService.logRequest(request, body);
        return super.afterBodyRead(body, inputMessage, parameter, targetType, converterType);
    }
}

3.4 返回参数处理类

./demo-web/src/main/java/com/ljq/demo/springboot/web/acpect/CustomResponseBodyAdviceAdapter.java
package com.ljq.demo.springboot.web.acpect;

import com.ljq.demo.springboot.common.log.LogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

/**
 * @Description: 返回参数处理类
 * @Author: junqiang.lu
 * @Date: 2020/1/8
 */
@ControllerAdvice
public class CustomResponseBodyAdviceAdapter implements ResponseBodyAdvice<Object> {

    @Autowired
    private LogService logService;

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  ServerHttpRequest request, ServerHttpResponse response) {
        if (request instanceof ServletServerHttpRequest && response instanceof ServletServerHttpResponse) {
            logService.logResponse(((ServletServerHttpRequest) request).getServletRequest(),
                    ((ServletServerHttpResponse) response).getServletResponse(), body);
        }
        return body;
    }
}

4 日志记录维度

从上边的代码中可以看出,日志的记录维度包括:

请求参数:

用户源 ip(requestIP)、请求数据类型(content-Type)、请求接口地址(requestUrl)、请求方式(requestMethod)、请求参数(requestParasm/requestBody)

返回参数:

用户源 ip(requestIP)、请求接口地址(requestUrl)、返回数据

说明:返回参数的日志记录也包括 requestIP 和 requestUrl 是为了方便在并发情况下能够区分出对应的请求参数和返回参数

5 请求测试

为避免干扰,测试前先将原来的 AOP 日志记录关闭

./demo-web/src/main/java/com/ljq/demo/springboot/web/acpect/LogAspect.java
//@Aspect
//@Component
public class LogAspect {

5.1 GET 请求

请求参数在 requestParams 中

请求链接:

http://127.0.0.1:8088/api/user/list?demoKey=demoValue

日志:

2020-01-10 14:44:45 | INFO  | http-nio-8088-exec-4 | c.ljq.demo.springboot.web.acpect.SimpleInterceptor 29| preHandle
2020-01-10 14:44:45 | INFO  | http-nio-8088-exec-4 | com.ljq.demo.springboot.common.log.LogService 28| [LOG-REQUEST]
	requestIP: 127.0.0.1
	contentType:application/x-www-form-urlencoded
	requestUrl: http://127.0.0.1:8088/api/user/list
	requestMethod: GET
	requestParams: {demoKey=[demoValue]}
	requestBody: null
2020-01-10 14:44:45 | DEBUG | http-nio-8088-exec-4 | c.l.d.springboot.dao.user.UserDao.queryListComplex 159| ==>  Preparing: select u.id, u.user_name, u.user_passcode, u.user_email, u.user_insert_time, u.user_update_time, u.user_status, u.user_version, u.user_del from `user` u where u.user_del = 0 ORDER BY id DESC LIMIT 0, 5 
2020-01-10 14:44:45 | DEBUG | http-nio-8088-exec-4 | c.l.d.springboot.dao.user.UserDao.queryListComplex 159| ==> Parameters: 
2020-01-10 14:44:45 | DEBUG | http-nio-8088-exec-4 | c.l.d.springboot.dao.user.UserDao.queryListComplex 159| <==      Total: 5
2020-01-10 14:44:45 | INFO  | http-nio-8088-exec-4 | com.ljq.demo.springboot.common.log.LogService 42| [LOG-RESPONSE]
	requestIp: 127.0.0.1
	requestUrl: http://127.0.0.1:8088/api/user/list
	response: ApiResult(code=200, msg=成功, data=[UserDO(id=5, userName=liming, userPasscode=ed0de7252acf2980e677bacab01bde25, userEmail=liming@example.com, userInsertTime=2019-06-12 17:42:40, userUpdateTime=2019-06-12 17:42:40, userStatus=1, userVersion=1, userDel=0), UserDO(id=4, userName=lily, userPasscode=ed0de7252acf2980e677bacab01bde25, userEmail=lily@example.com, userInsertTime=2019-06-12 17:42:40, userUpdateTime=2019-06-12 17:42:40, userStatus=1, userVersion=1, userDel=0), UserDO(id=3, userName=jack, userPasscode=ed0de7252acf2980e677bacab01bde25, userEmail=jack@example.com, userInsertTime=2019-06-12 17:42:40, userUpdateTime=2019-06-12 17:42:40, userStatus=1, userVersion=1, userDel=0), UserDO(id=2, userName=bob, userPasscode=ed0de7252acf2980e677bacab01bde25, userEmail=bob@example.com, userInsertTime=2019-06-12 17:42:40, userUpdateTime=2019-06-12 17:42:40, userStatus=1, userVersion=1, userDel=0), UserDO(id=1, userName=tom, userPasscode=ed0de7252acf2980e677bacab01bde25, userEmail=tom@example.com, userInsertTime=2019-06-12 17:42:40, userUpdateTime=2019-06-12 17:42:40, userStatus=1, userVersion=1, userDel=0)], extraData=null, timestamp=1578638685118)
2020-01-10 14:44:45 | INFO  | http-nio-8088-exec-4 | c.ljq.demo.springboot.web.acpect.SimpleInterceptor 38| postHandle
2020-01-10 14:44:45 | INFO  | http-nio-8088-exec-4 | c.ljq.demo.springboot.web.acpect.SimpleInterceptor 44| afterCompletion

5.2 POST 请求-Params

请求参数在 requestParams 中

请求链接:

http://127.0.0.1:8088/api/user/lists?demoKey=demoValue

日志:

2020-01-10 14:44:45 | INFO  | http-nio-8088-exec-4 | c.ljq.demo.springboot.web.acpect.SimpleInterceptor 38| postHandle
2020-01-10 14:44:45 | INFO  | http-nio-8088-exec-4 | c.ljq.demo.springboot.web.acpect.SimpleInterceptor 44| afterCompletion
2020-01-10 14:46:58 | DEBUG | Eureka-PeerNodesUpdater | com.netflix.discovery.endpoint.EndpointUtils 198| The availability zone for the given region us-east-1 are [defaultZone]
2020-01-10 14:46:58 | WARN  | Eureka-PeerNodesUpdater | com.netflix.eureka.cluster.PeerEurekaNodes 156| The replica size seems to be empty. Check the route 53 DNS Registry
2020-01-10 14:50:49 | INFO  | http-nio-8088-exec-6 | c.ljq.demo.springboot.web.acpect.SimpleInterceptor 29| preHandle
2020-01-10 14:50:49 | INFO  | http-nio-8088-exec-6 | com.ljq.demo.springboot.common.log.LogService 28| [LOG-REQUEST]
	requestIP: 127.0.0.1
	contentType:null
	requestUrl: http://127.0.0.1:8088/api/user/lists
	requestMethod: POST
	requestParams: {demoKey=[demoValue]}
	requestBody: null
2020-01-10 14:50:49 | DEBUG | http-nio-8088-exec-6 | c.l.d.springboot.dao.user.UserDao.queryListComplex 159| ==>  Preparing: select u.id, u.user_name, u.user_passcode, u.user_email, u.user_insert_time, u.user_update_time, u.user_status, u.user_version, u.user_del from `user` u where u.user_del = 0 ORDER BY id DESC LIMIT 0, 5 
2020-01-10 14:50:49 | DEBUG | http-nio-8088-exec-6 | c.l.d.springboot.dao.user.UserDao.queryListComplex 159| ==> Parameters: 
2020-01-10 14:50:49 | DEBUG | http-nio-8088-exec-6 | c.l.d.springboot.dao.user.UserDao.queryListComplex 159| <==      Total: 5
2020-01-10 14:50:49 | INFO  | http-nio-8088-exec-6 | com.ljq.demo.springboot.common.log.LogService 42| [LOG-RESPONSE]
	requestIp: 127.0.0.1
	requestUrl: http://127.0.0.1:8088/api/user/lists
	response: ApiResult(code=200, msg=成功, data=[UserDO(id=5, userName=liming, userPasscode=ed0de7252acf2980e677bacab01bde25, userEmail=liming@example.com, userInsertTime=2019-06-12 17:42:40, userUpdateTime=2019-06-12 17:42:40, userStatus=1, userVersion=1, userDel=0), UserDO(id=4, userName=lily, userPasscode=ed0de7252acf2980e677bacab01bde25, userEmail=lily@example.com, userInsertTime=2019-06-12 17:42:40, userUpdateTime=2019-06-12 17:42:40, userStatus=1, userVersion=1, userDel=0), UserDO(id=3, userName=jack, userPasscode=ed0de7252acf2980e677bacab01bde25, userEmail=jack@example.com, userInsertTime=2019-06-12 17:42:40, userUpdateTime=2019-06-12 17:42:40, userStatus=1, userVersion=1, userDel=0), UserDO(id=2, userName=bob, userPasscode=ed0de7252acf2980e677bacab01bde25, userEmail=bob@example.com, userInsertTime=2019-06-12 17:42:40, userUpdateTime=2019-06-12 17:42:40, userStatus=1, userVersion=1, userDel=0), UserDO(id=1, userName=tom, userPasscode=ed0de7252acf2980e677bacab01bde25, userEmail=tom@example.com, userInsertTime=2019-06-12 17:42:40, userUpdateTime=2019-06-12 17:42:40, userStatus=1, userVersion=1, userDel=0)], extraData=null, timestamp=1578639049810)
2020-01-10 14:50:49 | INFO  | http-nio-8088-exec-6 | c.ljq.demo.springboot.web.acpect.SimpleInterceptor 38| postHandle
2020-01-10 14:50:49 | INFO  | http-nio-8088-exec-6 | c.ljq.demo.springboot.web.acpect.SimpleInterceptor 44| afterCompletion

5.3 POST请求-Body

请求参数在 Body 中

请求链接:

http://127.0.0.1:8088/api/user/list

request Body:

{
	"page" : 1,
	"offset" : 1,
	"limit" : 2
}

日志:

2020-01-10 14:52:39 | INFO  | http-nio-8088-exec-8 | c.ljq.demo.springboot.web.acpect.SimpleInterceptor 29| preHandle
2020-01-10 14:52:39 | INFO  | http-nio-8088-exec-8 | com.ljq.demo.springboot.common.log.LogService 28| [LOG-REQUEST]
	requestIP: 127.0.0.1
	contentType:application/json
	requestUrl: http://127.0.0.1:8088/api/user/list
	requestMethod: POST
	requestParams: {}
	requestBody: {page=1, offset=1, limit=2}
2020-01-10 14:52:39 | DEBUG | http-nio-8088-exec-8 | c.l.d.springboot.dao.user.UserDao.queryListComplex 159| ==>  Preparing: select u.id, u.user_name, u.user_passcode, u.user_email, u.user_insert_time, u.user_update_time, u.user_status, u.user_version, u.user_del from `user` u where u.user_del = 0 ORDER BY id DESC LIMIT 0, 5 
2020-01-10 14:52:39 | DEBUG | http-nio-8088-exec-8 | c.l.d.springboot.dao.user.UserDao.queryListComplex 159| ==> Parameters: 
2020-01-10 14:52:39 | DEBUG | http-nio-8088-exec-8 | c.l.d.springboot.dao.user.UserDao.queryListComplex 159| <==      Total: 5
2020-01-10 14:52:39 | INFO  | http-nio-8088-exec-8 | com.ljq.demo.springboot.common.log.LogService 42| [LOG-RESPONSE]
	requestIp: 127.0.0.1
	requestUrl: http://127.0.0.1:8088/api/user/list
	response: ApiResult(code=200, msg=成功, data=[UserDO(id=5, userName=liming, userPasscode=ed0de7252acf2980e677bacab01bde25, userEmail=liming@example.com, userInsertTime=2019-06-12 17:42:40, userUpdateTime=2019-06-12 17:42:40, userStatus=1, userVersion=1, userDel=0), UserDO(id=4, userName=lily, userPasscode=ed0de7252acf2980e677bacab01bde25, userEmail=lily@example.com, userInsertTime=2019-06-12 17:42:40, userUpdateTime=2019-06-12 17:42:40, userStatus=1, userVersion=1, userDel=0), UserDO(id=3, userName=jack, userPasscode=ed0de7252acf2980e677bacab01bde25, userEmail=jack@example.com, userInsertTime=2019-06-12 17:42:40, userUpdateTime=2019-06-12 17:42:40, userStatus=1, userVersion=1, userDel=0), UserDO(id=2, userName=bob, userPasscode=ed0de7252acf2980e677bacab01bde25, userEmail=bob@example.com, userInsertTime=2019-06-12 17:42:40, userUpdateTime=2019-06-12 17:42:40, userStatus=1, userVersion=1, userDel=0), UserDO(id=1, userName=tom, userPasscode=ed0de7252acf2980e677bacab01bde25, userEmail=tom@example.com, userInsertTime=2019-06-12 17:42:40, userUpdateTime=2019-06-12 17:42:40, userStatus=1, userVersion=1, userDel=0)], extraData=null, timestamp=1578639159441)
2020-01-10 14:52:39 | INFO  | http-nio-8088-exec-8 | c.ljq.demo.springboot.web.acpect.SimpleInterceptor 38| postHandle
2020-01-10 14:52:39 | INFO  | http-nio-8088-exec-8 | c.ljq.demo.springboot.web.acpect.SimpleInterceptor 44| afterCompletion

5.4 POST请求-上传文件

POST 请求,上传文件

请求链接:

http://127.0.0.1:8088/api/demo/common/upload
file: {…}
_events: {}
_eventsCount: 3
_readableState: {…}
autoClose: true
bytesRead: 1420182
closed: true
domain: null
fd: null
flags: "r"
mode: 438
path: "F:\download\阿里巴巴Java开发手册(华山版).pdf"
readable: false
uploadKey: "uploadValue"

一份文件,一个 uploadKey 参数

日志:

2020-01-10 14:55:49 | INFO  | http-nio-8088-exec-1 | c.ljq.demo.springboot.web.acpect.SimpleInterceptor 29| preHandle
2020-01-10 14:55:49 | INFO  | http-nio-8088-exec-1 | com.ljq.demo.springboot.common.log.LogService 42| [LOG-RESPONSE]
	requestIp: 127.0.0.1
	requestUrl: http://127.0.0.1:8088/api/demo/common/upload
	response: ApiResult(code=200, msg=成功, data=null, extraData=null, timestamp=1578639349715)
2020-01-10 14:55:49 | INFO  | http-nio-8088-exec-1 | c.ljq.demo.springboot.web.acpect.SimpleInterceptor 38| postHandle
2020-01-10 14:55:49 | INFO  | http-nio-8088-exec-1 | c.ljq.demo.springboot.web.acpect.SimpleInterceptor 44| afterCompletion

没有请求参数相关的日志,只有返回参数日志

5.5 下载文件

请求连接

http://127.0.0.1:8088/api/demo/common/download?fileName=url

日志:

2020-01-10 15:06:14 | INFO  | http-nio-8088-exec-9 | c.ljq.demo.springboot.web.acpect.SimpleInterceptor 29| preHandle
2020-01-10 15:06:14 | INFO  | http-nio-8088-exec-9 | com.ljq.demo.springboot.common.log.LogService 28| [LOG-REQUEST]
	requestIP: 127.0.0.1
	contentType:application/x-www-form-urlencoded
	requestUrl: http://127.0.0.1:8088/api/demo/common/download
	requestMethod: GET
	requestParams: {fileName=[url]}
	requestBody: null
2020-01-10 15:06:14 | INFO  | http-nio-8088-exec-9 | com.ljq.demo.springboot.common.log.LogService 42| [LOG-RESPONSE]
	requestIp: 127.0.0.1
	requestUrl: http://127.0.0.1:8088/api/demo/common/download
	response: [-1, -40, -1, -32]
2020-01-10 15:06:14 | INFO  | http-nio-8088-exec-9 | c.ljq.demo.springboot.web.acpect.SimpleInterceptor 38| postHandle
2020-01-10 15:06:14 | INFO  | http-nio-8088-exec-9 | c.ljq.demo.springboot.web.acpect.SimpleInterceptor 44| afterCompletion

由于返回的日志将整个文件转化为 byte 打印出来,为了演示,作者将 response 进行了截取

6 优缺点

优点:

  • 使用 HandlerInterceptor@ControllerAdvice 便于开发者理解程序
  • 很好解决了读取 POST 请求 Body 中数据的问题
  • 能够实现较多维度的参数日志记录,便于日志分析与问题排查
  • 满足基本的 REST 接口出入参日志记录
  • 能够很好处理 Params 和 Body 的参数日志记录
  • 日志格式友好

缺点:

  • 不能完全保证请求参数日志与返回参数日志一一对应,在高并发请求下,多用户同一 ip 请求同一接口的情况是存在的
  • 暂未对文件相关操作做适配,上传文件与下载文件日志记录不完善
  • 使用了多个类才实现出入参的日志记录,较为繁琐

7 参考资料推荐

Logging Requests and Responses in Spring (including body)

Spring Boot - How to log all requests and responses with exceptions in single place?

Get RequestBody and ResponseBody at HandlerInterceptor

Spring – Log Incoming Requests

java在filter中获取POST请求中request参数以及解决ServletInputStream重复读取的问题

filter过滤器,解决request获取前端参数中,参数以json流的形式传输的问题

8 本次提交记录

commit 39518644867d5e969569a825ff42a9ef2b2e2c32 (HEAD -> dev, origin/master, origin/dev, origin/HEAD, master)
Author: flying9001 <flying9001@gmail.com>
Date:   Wed Jan 8 18:22:59 2020 +0800

    代码-新增一种 Spring Boot 日志记录解决方案

版本回退命令:

git reset --soft 39518644867d5e969569a825ff42a9ef2b2e2c32