往往我们记录http请求响应日志时,都习惯性用log.info等方式,随着业务代码量增加,代码可读性非常糟糕。这时我们可以借助servlet拦截器帮助我们来实现统一日志跟踪
直接上代码
package com.lmx.demo.filter;
import com.google.common.base.Charsets;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StreamUtils;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.*;
import java.util.List;
/**
* http报文日志拦截器
*/
@Slf4j
public class HttpLogTraceFilter implements Filter {
private List<String> ignoreUrlList;
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String uri = httpRequest.getRequestURI().trim().toLowerCase();
log.info("uri ={}", uri);
if (isIgnore(uri)) {
chain.doFilter(request, response);
} else {
byte[] byteData = StreamUtils.copyToByteArray(request.getInputStream());
String reqBody = new String(byteData, Charsets.UTF_8);
ServletOutputStreamWrap servletOutputStreamWrap = new ServletOutputStreamWrap();
LogWrapServletResponse servletResponseWrapper = new LogWrapServletResponse(response, servletOutputStreamWrap);
LogWrapServletRequest logWrapServletRequest = new LogWrapServletRequest(request, byteData);
log.info("reqBody={}", reqBody);
chain.doFilter(logWrapServletRequest, servletResponseWrapper);
String resp = servletOutputStreamWrap.get();
log.info("respBody={}", resp);
response.setContentType("application/json;charset=utf-8");
PrintWriter printWriter = response.getWriter();
printWriter.print(resp);
printWriter.flush();
printWriter.close();
}
}
@Override
public void init(FilterConfig arg0) throws ServletException {
String ignoreUrl = arg0.getInitParameter("ignoreUrl");
if (ignoreUrl == null) {
this.ignoreUrlList = Lists.newArrayList();
} else {
/**
* 忽略的uri,多个用逗号分隔
*/
this.ignoreUrlList = Lists.newArrayList(Splitter.on(",").split(ignoreUrl));
}
}
boolean isIgnore(String uri) {
for (String s : ignoreUrlList) {
if (uri.contains(s)) {
return true;
}
}
return false;
}
public class LogWrapServletRequest extends HttpServletRequestWrapper {
private byte[] req;
public LogWrapServletRequest(ServletRequest request, byte[] req) {
super((HttpServletRequest) request);
this.req = req;
}
public ServletInputStream getInputStream() {
return new ServletInputStreamWrap(new ByteArrayInputStream(req));
}
}
public static class ServletInputStreamWrap extends ServletInputStream {
private final InputStream sourceStream;
public ServletInputStreamWrap(InputStream sourceStream) {
this.sourceStream = sourceStream;
}
@Override
public int read() throws IOException {
return this.sourceStream.read();
}
@Override
public void close() throws IOException {
super.close();
this.sourceStream.close();
}
}
public static class LogWrapServletResponse extends HttpServletResponseWrapper {
private ServletOutputStreamWrap servletOutputStreamWrap;
public LogWrapServletResponse(ServletResponse response, ServletOutputStreamWrap servletOutputStreamWrap) {
super((HttpServletResponse) response);
this.servletOutputStreamWrap = servletOutputStreamWrap;
}
public ServletOutputStreamWrap getServletOutputStreamWrap() {
return servletOutputStreamWrap;
}
public ServletOutputStream getOutputStream() throws IOException {
return servletOutputStreamWrap;
}
}
@Slf4j
public static class ServletOutputStreamWrap extends ServletOutputStream {
private List<Byte> list = Lists.newArrayList();
public ServletOutputStreamWrap() {
}
@Override
public void write(int b) throws IOException {
list.add((byte) b);
}
public String get() {
byte[] resp = new byte[list.size()];
for (int i = 0; i < list.size(); i++) {
resp[i] = list.get(i);
}
try {
return new String(resp, Charsets.UTF_8.toString());
} catch (UnsupportedEncodingException e) {
log.error("", e);
return "";
}
}
}
}
配置拦截器
springMVC配置web.xml
springboot通过FilterRegistyBean注入
日志效果
2019-09-26 10:48:05.162 [http-apr-8080-exec-6] INFO
c.q.filter.HttpLogTraceFilter - uri =/xxx/xxxx.json
2019-09-26 10:48:05.162 [http-apr-8080-exec-6] INFO
c.q.filter.HttpLogTraceFilter - reqBody={"id":252,"date":"2019-09-26"}
2019-09-26 10:48:05.176 [http-apr-8080-exec-6] INFO
c.q.filter.HttpLogTraceFilter - respBody={"result":0,"message":""}