在开发中,需要记录接口的请求日志及返回日志,便于回溯原因。这里面请求日志为什么要单独拿出来讲,是因为当前是以post body的方式提交接口请求,body是以stream的方式接收,如果在拦截器中获取该body内容后,真实请求中将捕获不到该参数了,因此需要引入BodyReaderHttpServletRequestWrapper
来实现接口的请求日志记录。
pom.xml增加依赖
<dependency>
<groupId>org.jodd</groupId>
<artifactId>jodd-core</artifactId>
<version>3.9</version>
</dependency>
用于获取当前request中的stream信息
BodyReaderHttpServletRequestWrapper 重新包装request
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
/**
* 定义字节流
*/
private final byte[] body;
/**
* 把请求的http包装一下
*
* @param request request
* @throws IOException IO异常
*/
public BodyReaderHttpServletRequestWrapper(HttpServletRequest request)
throws IOException {
super(request);
// body = StreamUtil.readBytes(request.getReader(), JoddDefault.encoding);
// 因为http协议默认传输的编码就是iso-8859-1,如果使用utf-8转码乱码的话,可以尝试使用iso-8859-1
body = StreamUtil.readBytes(request.getReader(), "UTF-8");
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream bais = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
return bais.read();
}
};
}
}
HttpServletRequestReplacedFilter 新建过滤器替换request的body
public class HttpServletRequestReplacedFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//Do nothing
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
ServletRequest requestWrapper = null;
if (request instanceof HttpServletRequest) {
requestWrapper = new BodyReaderHttpServletRequestWrapper((HttpServletRequest) request);
}
chain.doFilter(requestWrapper, response);
}
@Override
public void destroy() {
//Do nothing
}
}
LogInterceptor利用拦截器来记录日志
public class LogInterceptor implements HandlerInterceptor {
/**
* 定义日志
*/
private static Logger logger = LoggerFactory.getLogger("operationLog");
/**
* 记录操作日志
*
* @param request request
* @param response response
* @param handler handler
* @return 处理是否成功
* @throws Exception 异常
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//校验权限
String path = request.getServletPath();
String contentType = request.getContentType();
if (contentType != null && contentType.equals("application/json")) {
// 只有json请求才记录日志,上传文件的不记录
//参数
String parameters = StringUtil.getBodyString(request.getReader());
User user = TokenUtil.getToken(request);
logOperation(path, parameters, user);
}
return true;
}
/**
* 记录文本日志
*
* @param path 路径
* @param parameters 参数值
* @param user user
*/
public void logOperation(String path, String parameters, User user) {
String log = "";
if (user == null) {
user = new User();
}
log = "[OPERALOG-操作日志]" + "-[" + user.getUsername() + "]" + "-[" + getSystemTime() + "]-" + "[INFO]-" + "-" + parameters;
logger.info(log);
}
public static String getSystemTime() {
String strTime = "";
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
strTime = df.format(new Date());
return strTime;
}
/**
* @param request request
* @param response response
* @param handler handler
* @param modelAndView modelAndView
* @throws Exception 异常
*/
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
/**
* @param request request
* @param response response
* @param handler handler
* @param ex 异常
* @throws Exception 异常
*/
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
这里块的代码非常简单,获取当前请求的路径及参数,记录下来,同时如果header中有authorization的话,再取当前的用户信息,这一块可以根据实际需要来修改。
LogConfig 加入需要记录日志的url
@Configuration
public class LogConfig extends WebMvcConfigurerAdapter {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//校验是否登录拦截器
registry.addInterceptor(new LogInterceptor())
//消息相关
.addPathPatterns("/news/*")
.addPathPatterns("/checkLogin");
super.addInterceptors(registry);
}
}
这一块和控制权限一样,增加一个InterceptorRegistry,并设置需要拦截的url串
请求的日志结果如下所示:
如果没有BodyReaderHttpServletRequestWrapper
对request的重新包装,直接获取body的参数,后续将会报错。