如何优雅的打印项目日志
框架
springboot
原理
使用filter拦截请求,打印出请求、响应,及耗时
知识点
1、OncePerRequestFilter
Filter base class that aims to guarantee a single execution per request dispatch, on any servlet container.Filter base class that aims to guarantee a single execution per request dispatch, on any servlet container.
过滤器基类,旨在保证在任何 servlet 容器上每个请求调度一次执行。
兼容多种servlet版本,保证在任何 servlet 容器上每个请求调度一次执行。
2、ContentCachingRequestWrapper,ContentCachingResponseWrapper
HttpServletRequest 包装器,用于缓存从输入流和读取器读取的所有内容,并允许通过字节数组检索此内容。
源代码如下,第一次获取输入流时,复制了一份
因为request的数据流只能读取一次,通过过滤器读取一次后,后面的业务处理会读不到数据
通过ContentCachingRequestWrapper将请求包装,可以进行多次读取
实现
继承OncePerRequestFilter,重写doFilterInternal方法
日志打印Filter
import com.google.common.base.Throwables;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
@Slf4j
public class LogFilter extends OncePerRequestFilter {
private Integer printMaxSize;
public LogFilter() {
}
public LogFilter(Integer printMaxSize) {
if (printMaxSize == null) {
printMaxSize = 1024;
}
this.printMaxSize = printMaxSize;
}
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
if (isAsyncDispatch(httpServletRequest)) {
filterChain.doFilter(httpServletRequest, httpServletResponse);
} else {
doLogFilter(getRequestWrapper(httpServletRequest), getResponseWrapper(httpServletResponse), filterChain);
}
}
private void doLogFilter(ContentCachingRequestWrapper requestWrapper, ContentCachingResponseWrapper responseWrapper, FilterChain filterChain) throws IOException, ServletException {
StringBuilder sb = new StringBuilder();
sb.append(System.lineSeparator())
.append("Method: [").append(requestWrapper.getMethod()).append("] ")
.append("URI: ").append(requestWrapper.getRequestURI())
.append(System.lineSeparator());
long start = System.currentTimeMillis();
filterChain.doFilter(requestWrapper, responseWrapper);
try {
sb.append(getParams(requestWrapper)).append(System.lineSeparator());
sb.append(getBody(requestWrapper));
sb.append(getResponse(responseWrapper)).append(System.lineSeparator());
} catch (Exception e) {
log.warn("日志打印失败 e:{}", Throwables.getStackTraceAsString(e));
} finally {
responseWrapper.copyBodyToResponse();
long end = System.currentTimeMillis();
sb.append("use time :").append(end - start).append("ms");
log.info(sb.toString());
}
}
private String getBody(ContentCachingRequestWrapper requestWrapper) {
StringBuilder sb = new StringBuilder();
String contentType = requestWrapper.getContentType();
if (requestWrapper.getMethod().equalsIgnoreCase("POST")
&& (MediaType.APPLICATION_JSON_VALUE.equals(contentType)
|| MediaType.APPLICATION_JSON_UTF8_VALUE.equals(contentType))) {
sb.append("body: ").append(System.lineSeparator());
sb.append(new String(requestWrapper.getContentAsByteArray(), StandardCharsets.UTF_8));
sb.append(System.lineSeparator());
}
return sb.toString();
}
private String getResponse(ContentCachingResponseWrapper responseWrapper) {
String responseStr = new String(responseWrapper.getContentAsByteArray(), StandardCharsets.UTF_8);
responseStr = responseStr.length() > printMaxSize ? responseStr.substring(0, printMaxSize) : responseStr;
return "response: " + responseStr;
}
private String getParams(ContentCachingRequestWrapper requestWrapper) {
Enumeration<String> enumeration = requestWrapper.getParameterNames();
StringBuilder sb = new StringBuilder();
sb.append("params: ");
while (enumeration.hasMoreElements()) {
String paramName = enumeration.nextElement();
sb.append(paramName);
sb.append(" : ");
sb.append(requestWrapper.getParameter(paramName));
sb.append(", ");
}
if (sb.length() > 2) {
sb.replace(sb.length() - 2, sb.length(), "");
}
return sb.toString();
}
private ContentCachingResponseWrapper getResponseWrapper(HttpServletResponse httpServletResponse) {
if (httpServletResponse instanceof ContentCachingResponseWrapper) {
return (ContentCachingResponseWrapper) httpServletResponse;
} else {
return new ContentCachingResponseWrapper(httpServletResponse);
}
}
private ContentCachingRequestWrapper getRequestWrapper(HttpServletRequest httpServletRequest) {
if (httpServletRequest instanceof ContentCachingRequestWrapper) {
return (ContentCachingRequestWrapper) httpServletRequest;
} else {
return new ContentCachingRequestWrapper(httpServletRequest, printMaxSize);
}
}
}
注入容器
@Configuration
public class FilterConfig {
/**
* 日志长度限制
*/
private Integer printMaxSize = 1024;
@Bean
public LogFilter getLogFilter() {
return new LogFilter(printMaxSize);
}
}