springboot打印接口调用日志的实例
短信预约 -IT技能 免费直播动态提醒
概述
请求日志几乎是所有大型企业级项目的必要的模块,请求日志对于我们来说后期在项目运行上线一段时间用于排除异常、请求分流处理、限制流量等。
请求日志一般都会记录请求参数、请求地址、请求状态(Status Code)、SessionId、请求方法方式(Method)、请求时间、客户端IP地址、请求返回内容、耗时等等。如果你得系统还有其他个性化的配置,也可以完成记录。
记录请求参数时,由于servlet.getInputStream的数据只能读取一次,因此需要先把数据缓存下来,构造返回流,保证之后的Controller可以正常读取到请求体的数据。
方案思路
- 封装HttpServletRequest请求类,改类在构造方法中将请求的输入流中的数据缓存了起来,保证之后的处理可以重复读取输入流中的数据。
- 实现过滤器,把上步封装的请求类传下去,保证Controller可以正常读取输入流中的数据。
- 添加拦截器,读取输入流中的数据。
- 读取返回参数。
封装HttpServletRequest请求
package com.example.demo.intercept;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
public 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 bytesBody = -1;
while ((bytesBody = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesBody);
}
} else {
stringBuilder.append("");
}
} catch (IOException e) {
} 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() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
ServletInputStream servletInputStream = 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 byteArrayInputStream.read();
}
};
return servletInputStream;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
public String getBody() {
return this.body;
}
}
把可重复读请求体通过过滤器往下传
防止请求流读取一次后就没有了,之后的不管是过滤器、拦截器、处理器都是读的已经缓存好的数据,实现可重复读。
package com.example.demo.intercept;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@Component
@WebFilter(urlPatterns = "
@Component
public class OperationLogInterceptor implements HandlerInterceptor {
public static final String JWT_SECERT = "23142d7a9s7d66970ad07d8sa";
private static List<String> pathList = new ArrayList<>();
static {
pathList.add("/mdms/model");
}
@Resource
private FunctionDOMapper functionDOMapper;//菜单动能sql
@Resource
private UserOperationHistoryDOMapper historyDOMapper;//操作日志记录表
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String servletPath = "" + request.getServletPath();
String method = request.getMethod();
pathList.forEach(path -> {
if (servletPath.contains(path)){
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
//获取token在请求中
if (cookie.getName().equals("_qjt_ac_")) {
String token = cookie.getValue();
byte[] encodeKey = Base64.decode(JWT_SECERT);
Claims claims = null;
try {
SecretKeySpec keySpec = new SecretKeySpec(encodeKey, 0, encodeKey.length, "AES");
claims = Jwts.parser().setSigningKey(keySpec).parseClaimsJws(token).getBody();
} catch (Exception e) {
return;
}
//用户账号
String account = claims.getSubject();
//查询URL在功能表中的功能
functionDOMapper.selectOne(servletPath, method);
//获取入参
RequestWrapper requestWrapper = null;
if (request instanceof HttpServletRequest) {
requestWrapper = new RequestWrapper(request);
}
Map<String,Object> map = new HashMap<>();
map.put("parameter", JSONObject.parse(requestWrapper.getBody()));
historyDOMapper.insert(map);//将操作信息入库
}
}
}
}
});
return true;
}
}
注册拦截器
package com.example.demo.config;
import com.example.demo.intercept.OperationLogInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
public class WebConfig implements WebMvcConfigurer {
@Bean
public HandlerInterceptor getOperationLogInterceptor() {
return new OperationLogInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(getOperationLogInterceptor()).addPathPatterns("
@ControllerAdvice(basePackages = "项目包")
public class GetResponseBody implements ResponseBodyAdvice<Object> {
public static final String JWT_SECERT = "23142d7a9s7d66970ad07d8sa";
private static List<String> pathList = new ArrayList<>();
static {
pathList.add("/mdms/model");
}
@Resource
private FunctionDOMapper functionDOMapper;//菜单动能sql
@Resource
private UserOperationHistoryDOMapper historyDOMapper;//操作日志记录表
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
return false;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
String path = serverHttpRequest.getURI().getPath();
String methodValue = serverHttpRequest.getMethodValue();
pathList.forEach(serverPath -> {
if (path.contains(serverPath)) {
HashMap<String, String> cookieMap = new HashMap<>();
HttpHeaders headers = serverHttpRequest.getHeaders();
List<String> cookieList = headers.get("cookie");
if (CollectionUtils.isEmpty(cookieList)) {
return;
}
String replaceAll = cookieList.get(0).replaceAll(";", "").replaceAll(";", "");
String[] split = replaceAll.split(";");
for (String cookie : split) {
String[] param = cookie.split("=");
cookieMap.put(param[0], param[1]);
}
String token = cookieMap.get("_qjt_ac_");
byte[] encodeKey = Base64.decode(JWT_SECERT);
Claims claims = null;
try {
SecretKeySpec keySpec = new SecretKeySpec(encodeKey, 0, encodeKey.length, "AES");
claims = Jwts.parser().setSigningKey(keySpec).parseClaimsJws(token).getBody();
} catch (Exception e) {
return;
}
//用户账号
String account = claims.getSubject();
//查询URL在功能表中的功能
functionDOMapper.selectOne(servletPath, method);
//获取返参
List<Object> list = historyDOMapper.select("功能表参数", account);
list.sort((Object1,Object2)->Object2.getTime().compareTo(Object1.getTime()));//将查询到的操作记录按时间降序排列
Object history = list.get(0);
if (body instanceof Response) {
Response response = (Response) body;
JSONObject jsonObject = JSONObject.parseObject(history.getParam());
jsonObject.put("message",response.getMessage());
jsonObject.put("body",response.getData());
history.setParam(jsonObject.toString());
history.setDes(response.getMessage());
}
historyDOMapper.updateByPrimaryKeySelective(history);//将操作信息更新
}
});
return body;
}
}
以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341