使用spring的aop来实现项目异常日志收集入库功能
相关注解
- @Aspect:用于定义切面
- @Before:通知方法会在目标方法调用之前执行
- @After:通知方法会在目标方法返回或抛出异常后执行
- @AfterReturning:通知方法会在目标方法返回后执行
- @AfterThrowing:通知方法会在目标方法抛出异常后执行
- @Around:通知方法会将目标方法封装起来
- @Pointcut:定义切点表达式
切点表达式
- 细粒度到方法
execution(方法修饰符 返回类型 方法所属的包.类名.方法名称(方法参数)
//org.jeecg.modules包及其子包下所有类中的public方法都应用切面里的通知
@Pointcut("execution(public * org.jeecg.modules..*.*Controller.*(..))")
- 细粒度到指定注解
//带@AutoLog注解的方法都应用切面里的通知
@Pointcut("@annotation(org.jeecg.common.aspect.annotation.AutoLog)")
注:前者应用广不需要在方法上声明就可以覆盖到,后者需要在每个方法都声明注解,可以在注解类配置个性化的参数。实际开发中需要根据不同业务场景使用两者
系统异常日志类
public class ExceptionLogDTO {
private Integer id;
/**请求方法*/
private String method;
/**请求说明*/
private Integer content;
/**异常信息*/
private String exMessage;
/**异常名称*/
private String exName;
/**错误类型*/
private Integer exType;
/**IP地址*/
private String ip;
/**参数*/
private String param;
/**是否删除*/
private Integer isDeleted;
/**创建人*/
private String createUserId;
/**创建时间*/
private Date createTime;
}
/**
* 异常收集
* @param joinPoint
* @param e
*/
@AfterThrowing(value = "logPointCut()",throwing = "e")
public void afterThrowable(JoinPoint joinPoint, Exception e) throws Throwable {
ExceptionLogDTO dto = new ExceptionLogDTO();
String message = stackTraceToString(e.getClass().getName(), e.getMessage(), e.getStackTrace());
dto.setExMessage(message);
dto.setExName(e.getMessage());
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
AutoLog syslog = method.getAnnotation(AutoLog.class);
//请求的方法名
String className = joinPoint.getTarget().getClass().getName();
String methodName = signature.getName();
dto.setMethod(className + "." + methodName + "()");
//设置请求说明
dto.setContent(getOperateType(methodName, syslog.operateType()));
//获取request
HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
//请求的参数
dto.setParam(getReqestParams(request,joinPoint));
//设置IP地址
dto.setIp(IPUtils.getIpAddr(request));
//获取登录用户信息
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
if(sysUser!=null){
dto.setCreateUserId(sysUser.getUsername());
}
//保存系统日志
baseCommonService.addExceptionLog(dto);
}
/**
* 转换异常信息为字符串
*
* @param exceptionName 异常名称
* @param exceptionMessage 异常信息
* @param elements 堆栈信息
*/
public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) {
StringBuffer strbuff = new StringBuffer();
for (StackTraceElement stet : elements) {
strbuff.append(stet + "\n");
}
String message = exceptionName + ":" + exceptionMessage + "\n\t" + strbuff.toString();
return message;
}
/**
* @Description: 获取请求参数
* @param request: request
* @param joinPoint: joinPoint
* @Return: java.lang.String
*/
private String getReqestParams(HttpServletRequest request, JoinPoint joinPoint) {
String httpMethod = request.getMethod();
String params = "";
if ("POST".equals(httpMethod) || "PUT".equals(httpMethod) || "PATCH".equals(httpMethod)) {
Object[] paramsArray = joinPoint.getArgs();
// java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
// https://my.oschina.net/mengzhang6/blog/2395893
Object[] arguments = new Object[paramsArray.length];
for (int i = 0; i < paramsArray.length; i++) {
if (paramsArray[i] instanceof BindingResult || paramsArray[i] instanceof ServletRequest || paramsArray[i] instanceof ServletResponse || paramsArray[i] instanceof MultipartFile) {
//ServletRequest不能序列化,从入参里排除,否则报异常:java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
//ServletResponse不能序列化 从入参里排除,否则报异常:java.lang.IllegalStateException: getOutputStream() has already been called for this response
continue;
}
arguments[i] = paramsArray[i];
}
PropertyFilter profilter = new PropertyFilter() {
@Override
public boolean apply(Object o, String name, Object value) {
if(value!=null && value.toString().length()>500){
return false;
}
return true;
}
};
params = JSONObject.toJSONString(arguments, profilter);
} else {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
// 请求的方法参数值
Object[] args = joinPoint.getArgs();
// 请求的方法参数名称
LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
String[] paramNames = u.getParameterNames(method);
if (args != null && paramNames != null) {
for (int i = 0; i < args.length; i++) {
params += " " + paramNames[i] + ": " + args[i];
}
}
}
return params;
}
接口测试
- 接口返回报错信息
- 数据库正确记录相关异常信息