这是我参与11月更文挑战的第11天,活动详情查看:2021最后一次更文挑战
前言
本节介绍RuoYi-Vue的模块中是如何进行操作日志切面的,这是一个很重要的地方,毕竟作为操作日志,可以帮助我们记录用户的各种操作,当出现问题时也可以很快的进行排查,但是怎样才是最优雅的记录日志的方式呢,最简单的方式是用注解来直接一步解决!
@Log(title = "参数管理", businessType = BusinessType.INSERT)
注解部分
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
/**
* 模块
*/
public String title() default "";
/**
* 功能
*/
public BusinessType businessType() default BusinessType.OTHER;
/**
* 操作人类别
*/
public OperatorType operatorType() default OperatorType.MANAGE;
/**
* 是否保存请求的参数
*/
public boolean isSaveRequestData() default true;
/**
* 是否保存响应的参数
*/
public boolean isSaveResponseData() default true;
}
可以看到,它是针对参数和方法起效的,默认是会保存请求和响应的参数,也可以通过设置为false来节省数据库的空间。
对应的处理逻辑
sys_oper_log表
当对应的方法有了注解之后,我们将如何进行日志的存储和处理呢?在Ruoyi中有一个sys_oper_log表来进行操作日志的记录。
对应的切面就是要将对应的数据放入其中,放的时机分别是
@AfterReturning和@AfterThrowing,从而能够在处理完请求后和出现异常之后进行日志的记录。
两者都是调用handleLog方法来进行日志的记录。handleLog方法就是将各种数据整理完毕之后放入表中。
获取请求参数
private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog) throws Exception {
String requestMethod = operLog.getRequestMethod();
if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) {
String params = argsArrayToString(joinPoint.getArgs());
operLog.setOperParam(StringUtils.substring(params, 0, 2000));
} else {
Map<?, ?> paramsMap = (Map<?, ?>) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
operLog.setOperParam(StringUtils.substring(paramsMap.toString(), 0, 2000));
}
}
如果是PUT和POST方法,直接用切面的joinPoint.getArgs()获取数据的字符串即可,否则要用ServletUtils.getRequest().getAttribute直接去请求里面拿对应的参数了。
获取参数的时候一定要注意参数中有没有文件对象,有的话不要字符串化,否则会报错的。要过滤掉,这里一定要注意多文件上传这个问题。
@SuppressWarnings("rawtypes")
public boolean isFilterObject(final Object o) {
Class<?> clazz = o.getClass();
if (clazz.isArray()) {
return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
} else if (Collection.class.isAssignableFrom(clazz)) {
Collection collection = (Collection) o;
for (Object value : collection) {
return value instanceof MultipartFile;
}
} else if (Map.class.isAssignableFrom(clazz)) {
Map map = (Map) o;
for (Object value : map.entrySet()) {
Map.Entry entry = (Map.Entry) value;
return entry.getValue() instanceof MultipartFile;
}
}
return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
|| o instanceof BindingResult;
}
首先对象的类型可能是数组,但是里面实际上是MultipartFile文件类型或者可能是Collection或者Map,其中包含着MultipartFile文件类型。