1. 方式一
直接继承OncePerRequestFilter
import com.alibaba.fastjson.JSON;
import com.util.IpUtil;
import org.slf4j.MDC;
import org.springframework.beans.BeanWrapper;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.*;
import java.util.*;
/**
* HTTP request logging
*/
public class RequestLoggerFilter extends OncePerRequestFilter {
private int maxPayloadLength = 25000;
private String getContentAsString(byte[] buf, int maxLength, String charsetName) {
if (buf == null || buf.length == 0) return "";
int length = Math.min(buf.length, this.maxPayloadLength);
try {
return new String(buf, 0, length, charsetName);
} catch (UnsupportedEncodingException ex) {
return "Unsupported Encoding";
}
}
@Override
protected void initBeanWrapper(BeanWrapper bw) {
}
/**
* Log each request and respponse with full Request URI, content payload and duration of the request in ms.
*
* @param request the request
* @param response the response
* @param filterChain chain of filters
* @throws ServletException 可能生成的异常
* @throws IOException 可能生成的异常
*/
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String url = request.getRequestURI();
// 1资源文件,前端视图文件,全部放过.childlist为配置列表,也删除
if (url.contains(".css") || url.contains(".js") || url.contains(".png") || url.contains(".jpg") || url.contains("/v/") || url.contains(".ico") || url.contains("/demo/") || url.contains("/swagger-resources") || url.contains("/v2") || url.contains("/index") || url.contains("/res") || url.contains("/swagger-ui") || url.contains("/webjars") || url.contains(".html") || url.contains(".css")) {
filterChain.doFilter(request, response);
return;
}
if (request.getMethod().toLowerCase().equals("get")) {
filterChain.doFilter(request, response);
return;
}
long startTime = System.currentTimeMillis();
StringBuffer reqInfo = new StringBuffer()
.append(request.getMethod())
.append(":")
.append(request.getRequestURL());
Long logId = System.currentTimeMillis() << 10;
Random rand = new Random(logId);
logId += rand.nextInt(1000);
MDC.put("logid", logId.toString());
Map<String, String> reqHeaderMap = new HashMap<>();
Enumeration<String> headers = request.getHeaderNames();
while (headers.hasMoreElements()) {
String headerName = headers.nextElement();
String headerValue = request.getHeader(headerName);
reqHeaderMap.put(headerName, request.getHeader(headerName));
}
String queryString = request.getQueryString();
if (queryString != null) {
reqInfo.append("?").append(queryString);
}
if (request.getAuthType() != null) {
reqInfo.append(", authType=")
.append(request.getAuthType());
}
if (request.getUserPrincipal() != null) {
reqInfo.append(", principalName=")
.append(request.getUserPrincipal().getName());
}
// ========= Log request and response payload ("body") ========
// We CANNOT simply read the request payload here, because then the InputStream would be consumed and cannot be read again by the actual processing/server.
// String reqBody = DoogiesUtil._stream2String(request.getInputStream()); // THIS WOULD NOT WORK!
// So we need to apply some stronger magic here :-)
ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request);
HttpServletResponseCopier wrappedResponse = new HttpServletResponseCopier(response);
// ======== This performs the actual request!
filterChain.doFilter(wrappedRequest, wrappedResponse);
//下面进行日志处理
long duration = System.currentTimeMillis() - startTime;
Map<String, String> rspHeaderMap = new HashMap<>();
Collection<String> rspHeaders = response.getHeaderNames();
for (String header : rspHeaders) {
rspHeaderMap.put(header, response.getHeader(header));
}
// I can only log the request's body AFTER the request has been made and ContentCachingRequestWrapper did its work.
byte[] reqContent = wrappedRequest.getContentAsByteArray();
String requestBody = this.getContentAsString(reqContent, this.maxPayloadLength, request.getCharacterEncoding());
byte[] respContent = wrappedResponse.getCopy();
String respBody = getContentAsString(respContent, this.maxPayloadLength, response.getCharacterEncoding());
reqHeaderMap.remove("accept-charset");
String log = String.format("[%d] ip[%s] req-header[%s] req-info[%s] req-body[%s] rsp-head[%s] rsp-body[%s] r-status[%s] t[%s]"
, logId
, IpUtil.getIpAddr(request)
, JSON.toJSONString(reqHeaderMap)
, reqInfo
, requestBody
, JSON.toJSONString(rspHeaderMap)
, respBody
, response.getStatus()
, duration);
logger.info(log);
// 重要:要把response的内容拷贝回原始响应中
wrappedResponse.flushBuffer();
}
public static class HttpServletResponseCopier extends HttpServletResponseWrapper {
private ServletOutputStream outputStream;
private PrintWriter writer;
private ServletOutputStreamCopier copier;
HttpServletResponseCopier(HttpServletResponse response) throws IOException {
super(response);
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
if (writer != null) {
throw new IllegalStateException("getWriter() has already been called on this response.");
}
if (outputStream == null) {
outputStream = getResponse().getOutputStream();
copier = new ServletOutputStreamCopier(outputStream, getResponse().getBufferSize());
}
return copier;
}
@Override
public PrintWriter getWriter() throws IOException {
if (outputStream != null) {
throw new IllegalStateException("getOutputStream() has already been called on this response.");
}
if (writer == null) {
copier = new ServletOutputStreamCopier(getResponse().getOutputStream(), getResponse().getBufferSize());
writer = new PrintWriter(new OutputStreamWriter(copier, getResponse().getCharacterEncoding()), true);
}
return writer;
}
@Override
public void flushBuffer() throws IOException {
if (writer != null) {
writer.flush();
} else if (outputStream != null) {
copier.flush();
}
}
byte[] getCopy() {
if (copier != null) {
return copier.getCopy();
} else {
return new byte[0];
}
}
}
public static class ServletOutputStreamCopier extends ServletOutputStream {
private OutputStream outputStream;
private ByteArrayOutputStream copy;
ServletOutputStreamCopier(OutputStream outputStream, int size) {
this.outputStream = outputStream;
this.copy = new ByteArrayOutputStream(size);
}
@Override
public void write(int b) throws IOException {
outputStream.write(b);
copy.write(b);
}
byte[] getCopy() {
return copy.toByteArray();
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setWriteListener(WriteListener listener) {
}
}
}
注入bean
/**
* 请求日志
*
* @return
*/
@Bean
public FilterRegistrationBean requestLoggerRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new RequestLoggerFilter());
registration.addUrlPatterns("/*");
registration.setOrder(3);
return registration;
}
``
工具类
```java
public class IpUtil {
/**
* 获取客户端IP
* @param request
* @return
*/
public static String getIpAddr(HttpServletRequest request) {
String ipAddress = request.getHeader("x-forwarded-for");
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
if (ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")) {
//根据网卡取本机配置的IP
InetAddress inet = null;
try {
inet = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
ipAddress = inet.getHostAddress();
}
}
//对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
if (ipAddress != null && ipAddress.length() > 15) { //"***.***.***.***".length() = 15
if (ipAddress.indexOf(",") > 0) {
ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
}
}
return ipAddress;
}
}
2. 方式二
还可以采用直接继承spring提供的CommonsRequestLoggingFilter
来打印请求日志。
CommonsRequestLoggingFilter
也间接继承了OncePerRequestFilter
package org.springframework.web.filter;
import javax.servlet.http.HttpServletRequest;
/**
* Simple request logging filter that writes the request URI
* (and optionally the query string) to the Commons Log.
*
* @author Rob Harrop
* @author Juergen Hoeller
* @since 1.2.5
* @see #setIncludeQueryString
* @see #setBeforeMessagePrefix
* @see #setBeforeMessageSuffix
* @see #setAfterMessagePrefix
* @see #setAfterMessageSuffix
* @see org.apache.commons.logging.Log#debug(Object)
*/
public class CommonsRequestLoggingFilter extends AbstractRequestLoggingFilter {
@Override
protected boolean shouldLog(HttpServletRequest request) {
return logger.isDebugEnabled();
}
/**
* Writes a log message before the request is processed.
*/
@Override
protected void beforeRequest(HttpServletRequest request, String message) {
logger.debug(message);
}
/**
* Writes a log message after the request is processed.
*/
@Override
protected void afterRequest(HttpServletRequest request, String message) {
logger.debug(message);
}
}
-
可以设置请求体里哪些内容可以被log
-
shouldLog判断是否可以log ,定制log前缀后缀
-
beforeRequest, afterRequest重写请求前后log的信息
使用:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.CommonsRequestLoggingFilter;
import javax.servlet.http.HttpServletRequest;
@Configuration
public class CalendarLoggingFilter extends CommonsRequestLoggingFilter {
private final Logger logger = LoggerFactory.getLogger(CalendarLoggingFilter.class);
private static long beforeTime;
private static long afterTime;
public MyDeskCalendarLoggingFilter(){
super.setIncludeClientInfo(false);
super.setIncludeHeaders(false);
super.setIncludePayload(true);
super.setMaxPayloadLength(2000);
super.setIncludeQueryString(true);
}
@Override
protected boolean shouldLog(HttpServletRequest request) {
String requestURI = request.getRequestURI();
// Checks if request matches /api/calendarCompletionDate
boolean shouldLog = requestURI.matches("^/api\\/calendarCompletionDate$");
if (shouldLog) {
long currentUserId = SecurityUtils.getCurrentAccountUserId();
String method = request.getMethod();
super.setBeforeMessagePrefix("Before request [" + method + "," + "currentUserId:" + currentUserId + ","); //Default is just Before request
super.setAfterMessagePrefix("After request [" + method + "," + "currentUserId:" + currentUserId + ","); //Default is just After request
}
return shouldLog;
}
@Override
protected void beforeRequest(HttpServletRequest request, String message) {
beforeTime = System.currentTimeMillis();
MemoryLogUtil.logUsed("beforeRequest");//only shows at this moment in time
super.beforeRequest(request, message);
}
@Override
protected void afterRequest(HttpServletRequest request, String message) {
afterTime = System.currentTimeMillis();
logger.info("afterRequest: Time taken: " + (afterTime-beforeTime) + " in milliseconds");
MemoryLogUtil.logUsed("afterRequest");//only shows at this moment in time
super.afterRequest(request, message);
}
}