在開發(fā)中,需要記錄接口的請求日志及返回日志,便于回溯原因。這里面請求日志為什么要單獨拿出來講,是因為當前是以post body的方式提交接口請求,body是以stream的方式接收,如果在攔截器中獲取該body內(nèi)容后,真實請求中將捕獲不到該參數(shù)了,因此需要引入BodyReaderHttpServletRequestWrapper 來實現(xiàn)接口的請求日志記錄。
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 {
/**
* 定義字節(jié)流
*/
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協(xié)議默認傳輸?shù)木幋a就是iso-8859-1,如果使用utf-8轉(zhuǎn)碼亂碼的話,可以嘗試使用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 {
//校驗權(quán)限
String path = request.getServletPath();
String contentType = request.getContentType();
if (contentType != null && contentType.equals("application/json")) {
// 只有json請求才記錄日志,上傳文件的不記錄
//參數(shù)
String parameters = StringUtil.getBodyString(request.getReader());
User user = TokenUtil.getToken(request);
logOperation(path, parameters, user);
}
return true;
}
/**
* 記錄文本日志
*
* @param path 路徑
* @param parameters 參數(shù)值
* @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 {
}
}
這里塊的代碼非常簡單,獲取當前請求的路徑及參數(shù),記錄下來,同時如果header中有authorization的話,再取當前的用戶信息,這一塊可以根據(jù)實際需要來修改。
LogConfig 加入需要記錄日志的url
@Configuration
public class LogConfig extends WebMvcConfigurerAdapter {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//校驗是否登錄攔截器
registry.addInterceptor(new LogInterceptor())
//消息相關(guān)
.addPathPatterns("/news/*")
.addPathPatterns("/checkLogin");
super.addInterceptors(registry);
}
}
這一塊和控制權(quán)限一樣,增加一個InterceptorRegistry,并設(shè)置需要攔截的url串
請求的日志結(jié)果如下所示:

日志結(jié)果.png
如果沒有BodyReaderHttpServletRequestWrapper對request的重新包裝,直接獲取body的參數(shù),后續(xù)將會報錯。