背景:
系统的操作日志、审计日志。在日常的管理还是维护中都会起到很大的作用。
解决办法:
可以在需要的方法中对日志进行保存操作,但是对业务代码入侵性大。
或者使用切面针对控制类进行处理,但是灵活度不高。
==》因此决定使用自定义注解 + 切面来针对方法进行日志记录。
目前日志主要记录的有三方面:
创建自定义注解 @AuditLog :
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
-
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.METHOD)
- public @interface AuditLog {
-
- // 1.操作描述
- String action() default "";
-
- // 2.操作类型(增删改查)
- OperateEnum type() default OperateEnum.MODIFY;
-
- // 3.是否记录参数
- boolean isRecord() default true;
- }
操作类型枚举类:新增、删除、修改、查询。
- public enum OperateEnum {
- ADD,
- DELETE,
- MODIFY,
- SAVE_OR_MODIFY,
- SELECT;
- }
创建一个切面类AuditAspect ,用于捕获带有@AuditLog 注解的方法调用并记录日志:
- import cn.hutool.json.JSONUtil;
- import lombok.extern.slf4j.Slf4j;
- import org.aspectj.lang.ProceedingJoinPoint;
- import org.aspectj.lang.annotation.Around;
- import org.aspectj.lang.annotation.Aspect;
- import org.aspectj.lang.annotation.Pointcut;
- import org.aspectj.lang.reflect.MethodSignature;
- import org.springframework.stereotype.Component;
-
- import javax.servlet.ServletRequest;
- import javax.servlet.ServletResponse;
- import java.lang.reflect.Method;
- import java.lang.reflect.Parameter;
- import java.util.HashMap;
-
- /**
- * 日志记录切面
- */
- @Slf4j
- @Aspect
- @Component
- public class AuditAspect {
-
- @Pointcut(value = "@annotation(com.bocloud.devops.eaas.api.log.AuditLog)")
- public void logMethodPointCut() {
- }
-
- @Around("logMethodPointCut()")
- public Object recordSysLog(ProceedingJoinPoint point) throws Throwable {
- UserLogRecordDO userLogRecordDO = bulidRequestParams(point);
-
- //先执行业务
-
- try {
- Object result = point.proceed();
- if (result != null) {
- userLogRecordDO.setResultMsg(JSONUtil.toJsonStr(result));
- }
- return result;
- } catch (BusinessException e) {
- userLogRecordDO.setResultCode(e.getCode());
- userLogRecordDO.setResultMsg(e.getMessage());
- throw e;
- } catch (Exception e) {
- userLogRecordDO.setResultCode("");
- userLogRecordDO.setResultMsg("后端未知异常");
- throw e;
- } finally {
- //操作日志入库
- try {
- //XXXService.save(userLogRecordDO);
- } catch (Exception e) {
- log.warn("记录用户操作异常:{}", e.getMessage());
- }
-
- }
- }
-
-
- /**
- * 记录方法和方法入参
- * @param point
- * @return
- */
- protected UserLogRecordDO bulidRequestParams(ProceedingJoinPoint point) {
- MethodSignature methodSignature = (MethodSignature) point.getSignature();
- Method method = methodSignature.getMethod();
- AuditLog annotation = method.getAnnotation(AuditLog.class);
-
- UserLogRecordDO userLogRecordDO = new UserLogRecordDO();
- userLogRecordDO.setAction(annotation.action());
- userLogRecordDO.setType(annotation.type().name());
- userLogRecordDO.setMethodName(method.getDeclaringClass().getSimpleName() + "." + method.getName());
-
- try {
- // 处理入参
- Parameter[] parameters = methodSignature.getMethod().getParameters();
- HashMap
paramMap = new HashMap<>(); - Object[] args = point.getArgs();
- for (int i = 0; i < parameters.length; i++) {
- AuditLog auditLog = parameters[i].getAnnotation(AuditLog.class);
- if (auditLog != null) {
- continue;
- }
-
- Class> type = parameters[i].getType();
- if (ServletResponse.class.isAssignableFrom(type) || ServletRequest.class.isAssignableFrom(type)) {
- continue;
- }
- String name = parameters[i].getName();
- paramMap.put(name, args[i]);
- }
-
- userLogRecordDO.setMethodParams(JSONUtil.toJsonStr(paramMap));
- return userLogRecordDO;
- } catch (Exception e) {
- log.warn("构建入参异常:{}", e.getMessage());
- }
- return userLogRecordDO;
- }
- }
logMethodPointCut(): 这个方法定义了一个切入点(pointcut),它使用@annotation注解来匹配带有com.bocloud.devops.eaas.api.log.AuditLog注解的方法。这意味着切面将在这些被标记为@AuditLog的方法执行前后生效。
recordSysLog(ProceedingJoinPoint point): 这是一个环绕通知方法,用于包围切入点方法的执行。它负责记录系统日志和审计信息。具体的操作包括:
- 构建请求参数并创建一个
UserLogRecordDO对象,其中包括操作描述、操作类型、方法名等信息。- 调用
point.proceed()来执行切入点方法,捕获方法的执行结果,将结果信息记录到UserLogRecordDO中。- 处理可能抛出的
BusinessException异常,记录异常信息。- 在
finally块中尝试将操作日志入库,但在此示例中,入库的部分被注释掉。
bulidRequestParams(ProceedingJoinPoint point): 这个方法用于构建方法的参数信息,并将其记录到UserLogRecordDO对象中。它包括方法名、操作描述、操作类型以及方法的入参信息。此方法通过反射分析方法参数和参数上的AuditLog注解,以构建参数信息的JSON表示。此切面的主要目的是在标记了
@AuditLog注解的方法执行前后记录操作日志信息。在实际应用中,您需要确保数据库操作以及记录日志的部分(在finally块中)按照实际需求进行配置。此示例代码中的入库部分被注释掉,您需要根据自己的需求实现相应的数据持久化逻辑。
- import io.swagger.annotations.ApiModelProperty;
- import io.swagger.annotations.ApiOperation;
- import lombok.Data;
-
- /**
- * @program: eaas-center
- * @description:
- * @author: yanchao
- * @create: 2023-10-10 23:09
- **/
- @Data
- @ApiOperation("操作日志记录表")
- public class UserLogRecordDO {
-
-
- @ApiModelProperty("主键")
- private String id;
-
- @ApiModelProperty("行为描述")
- private String action;
-
- @ApiModelProperty("执行方法")
- private String methodName;
-
- @ApiModelProperty("执行入参")
- private String methodParams;
-
- @ApiModelProperty("操作类型")
- private String type;
-
- @ApiModelProperty("响应编码")
- private String resultCode;
-
- @ApiModelProperty("结果描述")
- private String resultMsg;
-
- }
- /**
- * 通过id查询中心维护表
- *
- * @param id 主键
- * @return 单条数据
- */
- @GetMapping("{id}")
- @AuditLog(action = "通过id查询中心维护表", type = OperateEnum.SELECT)
- public Result
detail(@PathVariable("id") Integer id{ - return Result.success(this.eaasCentralMaintainService.detail(id));
- }
这样即可在数据库查看相应的操作日志记录。