数据业务级日志记录业务操作前后数据(springBoot)

        对于日志的记录,大家肯定会考虑使用aop,但是aop能不能记录业务操作前后的数据呢?今天跟大家分享的就是数据业务级日志记录的2个方案

 一、数据业务级日志是什么意思

        我这里的意思其实就是业务操作前的数据、业务操作后的数据。意义在于,可以通过对比直观显示业务操作前后字段值变化情况,方便运维、客服人员查看,同时出现事故可以通过日志追责。

二、设计思路

       1、使用aop,想办法记录业务操作前后的数据

       2、业务方法调用日志保存方法

三、使用aop实现记录操作前后业务数据

        aop的集成:

        pom文件引入aop支持

       
           org.springframework.boot
           spring-boot-starter-aop
        

       切面类:

@Aspect
@Component
@Slf4j
public class AppLogAop {
    @Pointcut("execution(public * com.xw.controller..*.*(..))")
    public void Pointcut() {
    }
 
    //前置通知
    @Before("Pointcut()")
    public void beforeMethod(JoinPoint joinPoint){
        log.info("调用了前置通知");
 
    }
 
    //@After: 后置通知
    @After("Pointcut()")
    public void afterMethod(JoinPoint joinPoint){
        log.info("调用了后置通知");
    }
    //@AfterRunning: 返回通知 rsult为返回内容
    @AfterReturning(value="Pointcut()",returning="result")
    public void afterReturningMethod(JoinPoint joinPoint,Object result){
        log.info("调用了返回通知");
    }
    //@AfterThrowing: 异常通知
    @AfterThrowing(value="Pointcut()",throwing="e")
    public void afterReturningMethod(JoinPoint joinPoint, Exception e){
        log.info("调用了异常通知");
    }
 
    //@Around:环绕通知
    @Around("Pointcut()")
    public Object Around(ProceedingJoinPoint pjp) throws Throwable {
        //TODO 切面记录日志:对于数据业务级的日志,缺陷是方法没有执行也会记录(方法里有很多校验)
        /*
        Signature sig = pjp.getSignature();
        MethodSignature msig = null;
        if (!(sig instanceof MethodSignature)) {
            throw new IllegalArgumentException("该注解只能用于方法");
        }
        msig = (MethodSignature) sig;
        Object target = pjp.getTarget();
        Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
        String methodName = currentMethod.getName();
        
        Object targetName = pjp.getTarget().getClass().getName();
        
        Object[] args = pjp.getArgs();
        
        
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        
        OperateLog operateLog = OperateLogUtil.initOperateLog(methodName,targetName,args,request);
        */
        
        log.info("around执行方法之前");
        Object object = pjp.proceed();
        log.info("around执行方法之后--返回值:" +object);
        return object;
    }

}

注意注解标签@Aspect、@Component,然后@Pointcut里注意设置controller包路径,具体的这里不细说。

主要看我的环绕事件Around,这个事件在Object object = pjp.proceed();前后做文章,在这之前是还没有执行业务方法,这之后是执行了。这里的思路:

1、取到方法名称、请求路径、controller等信息

2、定义一个枚举类,设置方法名、controller名、业务中文描述,凡是配置在这个枚举类的就是要记录的,否则就直接放过(更灵活一点的做法就是建表记录,考虑性能问题的化,可以将表记录的信息缓存到redis,避免查询数据库影响接口性能)

3、方法执行前初始一条日志记录数据,这个里面会根据controller、方法名称获取到对应的service或mapper,从入参获取id,查询业务数据。

4、方法执行后调用异步方法(使用多线程实现,上期分享讲过),同3一样查询数据(此时应该是已经操作过的数据,其实也可以直接在方法执行前就调用异步方法,查询的数据作为操作之前,用入参替换对应字段的值作为之后的数据,这样性能更优,对接口可以说是没有一点延迟影响)

5、保存生成的日志记录,这里操作之前、操作之后都使用json字符串记录数据,增加一个className,方便后面可以将json字符串转对象。

我的日志记录对象:

@Data
public class OperateLog {
    /**
     * 业务内部区分id
     */
    private String difSourceId;
    /**
     * 流水号
     */
    private Long operateNo;
    /**
     * 操作类别
     */
    private String operateType;
    /**
     * 数据json串,操作之前
     */
    private String beforeData;
    /**
     * 数据json串,操作之后
     */
    private String afterData;
    /**
     * 请求url
     */
    private String requestUrl;
    /**
     * 方法名称
     */
    private String methodName;
    /**
     * 操作人id
     */
    private Integer userId;
    /**
     * 操作人名称(冗余)
     */
    private String userName;
    /**
     * 记录时间
     */
    @JSONField(format = "yyyy-MM-dd HH:mm:ss")
    private String recordTime;
    
    /**
     * 参数接收,开始时间
     */
    @JSONField(format = "yyyy-MM-dd HH:mm:ss")
    private String startTime;
    /**
     * 参数接收,接收时间
     */
    @JSONField(format = "yyyy-MM-dd HH:mm:ss")
    private String endTime;
    /**
     * 关键字
     */
    private String paramKey;
    /**
     * 请求ip
     */
    private String reqIp;
    /**
     * 对象class名称
     */
    private String entityName;
    /*
     * 操作名称
     */
    private String operateName;
}

字段上有注释,相信大家一看就懂。

四、业务系统调用

     这个就更好解释了,实际就是每个方法里直接调用一个公共方法,传入之前、之后的json字符串、操作中文描述等,当然也是异步方法,不影响接口性能的。这个公共方法应该不用我贴代码大家就可以自己实现。

总结:

使用aop会简单点,每个service里的逻辑还是改怎么写就怎么写。但是如果遇到方法里操作多张业务数据表,那这就有点尴尬了。另外呢,方法里面还有参数校验嘛,可能参数校验后就不执行操作了呢?aop是无法感知是否真正执行了吧,也许有人说可以看返回结果啊,但是如果这样,你就不能开启异步方法了,对性能会有影响的,接口的执行时间肯定会边长。因此,我这里最终还是放弃了aop这种方法,使用了封装公共方法,之前批量初始日志,开启异步方法保存日志。

      希望能启发到大家,有更好的思路还原评论指正。

 

 

你可能感兴趣的:(Spring,Aop,springboot)