日常开发工作中避免不了要打印请求日志,这个功能几乎在所有的项目中都需要编写一次,重复的次数多了,难免会感觉繁琐,因此打算搞一个通用类把这块功能拆出来。
废话不多说——先上代码。
@WebFilter(filterName = "logFilter", urlPatterns = "/planet/*")
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
@Component
public class LogFilter implements Filter {
private static final Set<String> IGNORE_PATHS = Collections.unmodifiableSet(new HashSet<>(
Arrays.asList("/**/swagger-ui/**", "/**/swagger-resources/**", "/**/api-docs")));
@SneakyThrows
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) {
HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
// 忽略文件上传
String contentType = httpServletRequest.getHeader("content-type");
if (contentType != null && contentType.startsWith(MediaType.MULTIPART_FORM_DATA_VALUE)) {
filterChain.doFilter(httpServletRequest, httpServletResponse);
return;
}
// 忽略指定url
String path = httpServletRequest.getRequestURI().substring(httpServletRequest.getContextPath().length()).replaceAll("[/]+$", "");
PathMatcher matcher = new AntPathMatcher();
boolean isIgnore = IGNORE_PATHS.stream().anyMatch(ignore -> matcher.match(ignore, path));
if (isIgnore) {
filterChain.doFilter(httpServletRequest, httpServletResponse);
return;
}
ResponseWrapper wrapperResponse = new ResponseWrapper(httpServletResponse);
RequestWrapper requestWrapper = new RequestWrapper(httpServletRequest);
long before = System.currentTimeMillis();
log.info("========================================== Request Start ==========================================");
log.info("URL : {}", httpServletRequest.getRequestURL().toString());
Map<String, String> headers = new HashMap<>(32);
Enumeration<String> names = httpServletRequest.getHeaderNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
headers.put(name, httpServletRequest.getHeader(name));
}
log.info("Headers : {}", headers);
log.info("HTTP Method : {}", httpServletRequest.getMethod());
log.info("IP : {}", httpServletRequest.getRemoteAddr());
log.info("Request Body : {}", JSON.toJSON(requestWrapper.getBody()));
filterChain.doFilter(requestWrapper, wrapperResponse);
byte[] content = wrapperResponse.getContent();
log.info("Response Status : {}", wrapperResponse.getStatus());
log.info("Response Content : {}", JSON.toJSON(new String(content)));
log.info("Time-Consuming : {} ms", System.currentTimeMillis() - before);
log.info("=========================================== End ===========================================");
ServletOutputStream out = httpServletResponse.getOutputStream();
out.write(content);
out.flush();
}
}
class ResponseWrapper extends HttpServletResponseWrapper {
private final ByteArrayOutputStream buffer;
private final ServletOutputStream out;
public ResponseWrapper(HttpServletResponse httpServletResponse) {
super(httpServletResponse);
buffer = new ByteArrayOutputStream();
out = new WrapperOutputStream(buffer);
}
@Override
public ServletOutputStream getOutputStream() {
return out;
}
@Override
public void flushBuffer() throws IOException {
if (out != null) {
out.flush();
}
}
public byte[] getContent() throws IOException {
flushBuffer();
return buffer.toByteArray();
}
static class WrapperOutputStream extends ServletOutputStream {
private final ByteArrayOutputStream bos;
public WrapperOutputStream(ByteArrayOutputStream bos) {
this.bos = bos;
}
@Override
public void write(int b) {
bos.write(b);
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setWriteListener(WriteListener arg0) {
}
}
}
class RequestWrapper extends HttpServletRequestWrapper {
private final String body;
public RequestWrapper(HttpServletRequest request) {
super(request);
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
InputStream inputStream = null;
try {
inputStream = request.getInputStream();
if (inputStream != null) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
char[] charBuffer = new char[128];
int bytesRead;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
}
} catch (IOException ignored) {
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
body = stringBuilder.toString();
}
@Override
public ServletInputStream getInputStream() {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
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() {
return byteArrayInputStream.read();
}
};
}
@Override
public BufferedReader getReader() {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
public String getBody() {
return this.body;
}
}
这个类是一个过滤器,用来在Request与Response之间打印请求与响应日志。
tip:一次编写、处处粘贴。
众所周知,Servlet用来处理用户请求并且对用户请求进行响应,而Filter则是在请求达到Servlet之前,服务器响应返回到Servlet之前对数据进行的一次预处理。
因为request、response都是以流的形式进行传输的,而流有一个特性就是只允许读取一次,因此需要对Request与Response进行一次扩展,使其支持多次读取,其实现方式为:在第一次读取的时候将数据缓存起来,后续读取时直接读取缓存的内容。
过滤器虽然功能比较强大,但是其影响面也是十分巨大的。
因为过滤器会在所有的请求前后做一些预处理操作,如果预处理操作比较耗时则会降低部分系统性能(QPS、TPS)。
Ctrl+C之前,记得先点个赞哦。