生产系统在绝对数情况下无法远程到服务器情况下,定位生产问题变得异常困难,为了解决此问题我们可以在系统异常时候把请求参数、异常信息等插入数据库中,这样就算无法远程上服务器也能很方便的定位生产问题
通过HttpServletRequestWrapper包装类每次读取参数后再回写参数
public class RequestWrapper extends HttpServletRequestWrapper {
/**
* 请求对象body
*/
private byte[] body;
public RequestWrapper(HttpServletRequest request) throws IOException {
super(request);
body = StreamUtils.copyToByteArray(request.getInputStream());
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener listener) {
}
@Override
public int read() {
return byteArrayInputStream.read();
}
};
}
public byte[] getBody() {
return body;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
}
建立ThreadLocal存储请求参数
public class RequestBodyContextUtil {
private static final ThreadLocal<String> BODY_THREAD_LOCAL = new ThreadLocal<>();
private RequestBodyContextUtil() {
}
public static String getParam() {
return BODY_THREAD_LOCAL.get();
}
public static void setParam(String param) {
BODY_THREAD_LOCAL.set(param);
}
public static void clear() {
BODY_THREAD_LOCAL.remove();
}
}
加入过滤器
@Order(1)
@Component
@WebFilter(filterName = "RequestBodyFilter")
public class RequestBodyFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
String contentType = request.getContentType();
//是否为Multipart类型表单,此类型表单用于文件上传
//@RequestParam接收参数时,为X-WWw-form-urlencoded形式如: [jobGroup=0&jobId=0&logstatus=-1&start=01
// @PathVariable、@PathParam暂时不支持
if (ServletUtil.isMultipart((HttpServletRequest) request)
|| CharSequenceUtil.contains(contentType, ContentType.FORM_URLENCODED.getValue())) {
// 以上两种方式都是通过qetParameterMap获取请求参数
RequestBodyContextUtil.setParam(JSONUtil.toJsonStr(request.getParameterMap()));
chain.doFilter(request, response);
return;
}
RequestWrapper RequestWrapper = new RequestWrapper((HttpServletRequest) request);
// Rest请求JSON编码形式,通过body获取请求参数,其他的Rest形式暂时不支持如: ContentType.XML
if (CharSequenceUtil.contains(contentType, ContentType.JSON.getValue())) {
RequestBodyContextUtil.setParam(Strutil.str(requestWrapper.getBody(), Charset.defaultCharset()));
}
chain.doFilter(RequestWrapper, response);
}
}
请求都会经过上面的过滤器会将每个HttpServletRequest请求转换为包装类RequestWrapper,然后放入ThreadLocal中
返回对象
@Data
public final class Response <T> {
/**
* 响应码
*/
private String code;
/**
* 响应消息
*/
private String message;
/**
* 响应数据
*/
private T data;
}
全局异常
@Slf4j
@RestControllerAdvice
public class CustomExceptionHandler {
// 校验异常 省略注释
@ExceptionHandler(MethodArgumentNotValidException.class)
public Response handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
......省略代码......
// 从ThreadLocal获取请求参数,获取到对应请求可存入数据库中,方便定位异常
RequestBodyContextUtil.getParam();
}
// 数据库访问异常 省略注释
@ExceptionHandler(DataAccessException.class)
public Response handleDataAccessException(DataAccessException dataAccessException) {
......省略代码......
// 从ThreadLocal获取请求参数,获取到对应请求可存入数据库中,方便定位异常
RequestBodyContextUtil.getParam();
}
// 数据库访问超时 省略注释
@ExceptionHandler(QueryTimeoutException.class)
public Response handleDataAccessException(QueryTimeoutException queryTimeoutException {
......省略代码......
// 从ThreadLocal获取请求参数,获取到对应请求可存入数据库中,方便定位异常
RequestBodyContextUtil.getParam();
}
// 业务异常、省略注释
@ExceptionHandler(ServiceException.class)
public Response handleServiceException(ServiceException serviceException) {
......省略代码......
// 从ThreadLocal获取请求参数,获取到对应请求可存入数据库中,方便定位异常
RequestBodyContextUtil.getParam();
}
// 省略注释(兜底异常)
@ExceptionHandler(Exception.class)
public Response handleException(Exception exception) {
......省略代码......
// 从ThreadLocal获取请求参数,获取到对应请求可存入数据库中,方便定位异常
RequestBodyContextUtil.getParam();
}
}