操作留痕功能实现与探讨

news2024/9/30 3:34:29

操作留痕功能实现与探讨

背景

接手了一个单体应用项目,看系统介绍,说实现了【高性能的操作日志留痕】功能,就有点好奇它是怎么设计的,是阻塞队列还是怎样的线程池。结果我打开代码一看,真的是笑洗个人了。它是做了一个接口给前端去调用,代码和多线程一分钱关系都没有,直接是调用到接口,拼装了一下内容就入库了。这玩意哪里高性能了,url远程调用性能高?同步代码高?倒不是说这种方案完全不可取,文末会分析一下这适用于什么场景和优化建议。

最常用操作留痕设计

基于后端去实现操作留痕都会不可避免地对原代码有侵入性,但是我们要想出入侵性最低的方案——基于注解去实现。这么一来我们只需要在方法上加上一个注解和一些参数的设置,就可以完成操作日留痕功能的嵌入了。

操作留痕相对于主业务来说,它并不是重要的部分,所以我们没必要去等待操作留痕功能入库完成后再返回数据。操作留痕对于用户来说是无感的,应该用异步去实现。

编码实现

1.罗列操作留痕需要记录什么信息,哪些信息是可以自动获取的,哪些是要Hard Code的

package org.example.springboot.job.domain;

import lombok.Data;

import java.util.Date;

@Data
public class OptLogRecord {
    private Long id;    		// 主键
    private String userName; 	// 操作人员
    private String userId;		// 用户id

    private String optFunc;     // 菜单功能模块
    private String menuTitle;    // 具体的菜单
    private String optButton;     // 按钮

    private String status;      // 操作结果

    private Date optTime;       // 操作时间

    private String optReturn;   // 返回结果

}

以上是最基本的操作留痕信息,进阶考虑的话,可以有:请求参数、操作ip、操作地点、请求url、失败后的错误信息等。

其中,功能模块菜单按钮 这些业务类的信息是我们程序中无法区分的。当然,如果你能维护非常完美的方法名与业务信息的匹配枚举的话,那么我们可以通过切点获取方法名再映射出对应的业务信息。嗯,确实不错,就是有点麻烦。在这里我就使用Hard Code的方式去实现了。有需要的可以自己去实现方法名与业务信息的映射关系。

2.创建注解

package org.example.springboot.job.annotation;

import java.lang.annotation.*;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OptLog {

    String optFunc() default "";	// 功能模块
    String menuTitle() default "";	// 菜单标题
    String optButton() default "";	// 按钮

}

3.对使用了注解的方法进行切面织入

导入依赖

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.6</version>
</dependency>

创建切面类

@Aspect
@Component
public class OptLogAspect {
    
}

以下代码内容均为OptLogAspect中的内容,为了方便步骤讲解,拎出来说明

  1. 指明该切面指向的切点为OptLog

        @Pointcut("@annotation(org.example.springboot.job.annotation.OptLog)")
        public void optLogPointCut() {}
    
  2. 我们需要在使用注解的方法执行完成之后再记录他操作的信息

        // 这个result是使用了注解的方法的返回值
        @AfterReturning(pointcut = "optLogPointCut()", returning = "result")
        public void doAfterReturning(JoinPoint joinPoint, Object result) {
            handleOptLog(joinPoint, result, "SUCCESS");
        }
    

    这个handleOptLog方法还没实现,别急

  3. 如果出现了异常呢,那肯定是操作失败了,我们也要记录操作失败的信息

        @AfterThrowing(value = "optLogPointCut()")
        public void doAfterThrowing (final JoinPoint joinPoint) {
            handleOptLog(joinPoint, null, "FAILED");
        }
    
  4. 实现handleOptLog方法

        protected void handleOptLog(final JoinPoint joinPoint, Object result, String status) {
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            OptLog optLog = methodSignature == null ? null : methodSignature.getMethod().getAnnotation(OptLog.class);
            if (optLog == null) return;
    
            // TODO:获取当前用户 根据自己系统方式去获取用户
    
            OptLogRecord record = new OptLogRecord();
            // TODO: 设置用户名与用户id略过
    
            record.setStatus(status);    // 根据不同的切面来区分是否操作成功
            record.setOptReturn(result.toString());
            
            record.setOptTime(new Date());
    
            // 读取使用注解时的参数
            record.setOptFunc(optLog.optFunc());
            record.setMenuTitle(optLog.menuTitle());
            record.setOptButton(optLog.optButton());
    
            // 异步保存数据库
            TimerTask task = new TimerTask() {
                @Override
                public void run() {
                    // insert语句保存到数据库
                    System.out.println("insert:" + record.toString());
                }
            };
            ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
            executorService.schedule(task, 10, TimeUnit.MILLISECONDS);
        }
    

    这里的异步日志是使用定时任务线程去完成,延时10ms执行,不影响原本的方法执行效率。

注解使用示范

    @GetMapping("/test")
    @OptLog(optFunc = "系统管理", menuTitle = "菜单管理", optButton = "添加菜单")
    public String testCall() throws InterruptedException {
        Thread.sleep(1000);
        log.info("hello");
        AtomicInteger integer = new AtomicInteger();
        return "test";
    }

睡眠1秒模拟非常复杂的业务执行时间,测试结果如下

在这里插入图片描述

总结

到此该操作留痕功能基本已经实现完成,有些地方还需要继续优化和规范化。比如使用枚举、常量去规范一些Hard Code。

回到上面的问题,这种通过调用接口去实现操作留痕的方案,适用于对后端业务无法切入的情况。比如你开发的是一款具备身份权限校验等功能的快速框架,别人通过调用你的jar包即可改造自己已有的系统。为了方便别人的开发,不对已有代码进行过多的改造,那就只能通过这种调接口的方式实现操作留痕。

前端基于axios的拦截器,对发起的请求进行拦截,将操作记录信息通过接口调用的方式保存。这种方案的最大缺点就是对于大多数的业务场景都需要多进行一次http请求,这种性能开销不可忽视。如果说前端对操作留痕的http请求使用异步,那么日志留痕的可靠性也会降低不少,但随之提升的性能也会非常多。

当http请求到达后端时,后端采用异步入库,也可以提升不少的性能,但前端就无法得知操作留痕是否完成。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/381125.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Mysql全解[基础篇]

目录MySQL概述数据库相关概念MySQL数据库版本docker部署单机节点sqlmode说明连接mysql数据模型关系型数据库&#xff08;RDBMS&#xff09;数据模型mysql版本对比MySQL 5.5MySQL 5.6MySQL 5.7MySQL 8.0SQLSQL通用语法SQL分类DDL数据库操作表操作数据类型数值类型字符串类型日期…

操作系统——12.处理机调度的概念、层次

这篇文章我们来讲一下处理机调度的概念和层次 目录 1.概述 2.调度的基本概念 3.调度的三个层次 3.1高级调度 3.2中级调度 3.3.低级调度 3.4三种调度的对比 4.挂起状态与七状态模型 5.小结 1.概述 首先&#xff0c;我们来看一下本节的知识框架图&#xff1a; 2.调度的…

连接Oracle数据库失败(ORA-12514)故障排除

文章目录症状产生原因解决办法欢迎加下方我的微信&#x1f447;&#xff0c;拉你入学习群点击试看博主的专著《MySQL 8.0运维与优化》&#xff08;清华大学出版社&#xff09;ORA-12514的故障是很多新手在连接Oracle数据库时经常遇到故障&#xff0c;它通常表示无法连接到数据库…

python+pytest接口自动化框架(5)-requests发送post请求

在HTTP协议中&#xff0c;与get请求把请求参数直接放在url中不同&#xff0c;post请求的请求数据需通过消息主体(request body)中传递。且协议中并没有规定post请求的请求数据必须使用什么样的编码方式&#xff0c;所以其请求数据可以有不同的编码方式&#xff0c;服务端通过请…

专访阮健丨压力就是动力,危机也是转机

48岁的阮健加入酷雷曼已经三个年头&#xff0c;从最初的孤身一人至今&#xff0c;他已经拥有了一个五六人的VR全景团队。 阮健团队的拍摄设备 “人的一生真的是充满不确定性的&#xff0c;那些年我辗转好几个行业的时候&#xff0c;也会一边抱怨行业不景气一边又想着怎么多干点…

Vue3 企业级项目实战:全新优化升级

Vue3 企业级项目实战 - 程序员十三 - 掘金小册Vue3 Element Plus Spring Boot 企业级项目开发&#xff0c;升职加薪&#xff0c;快人一步。。「Vue3 企业级项目实战」由程序员十三撰写&#xff0c;2744人购买https://s.juejin.cn/ds/S2RkR9F/ 写下这些文字的时候是2022年9月…

无需公网IP,安全稳定实现U8C异地访问

用友是全球领先的企业云服务与软件提供商&#xff0c;在财务、人力、供应链、采购、制造、营销、研发、项目、资产、协同等领域为客户提供数字化、智能化、社会化的企业云服务产品与解决方案。 U8C是用友针对成长型、创新型企业&#xff0c;提供企业级ERP整体解决方案。在系统…

ChunJun 1.16 Release版本即将发布,bug 捉虫活动邀您参与!

亲爱的社区小伙伴们&#xff0c;历时数月&#xff0c;我们很高兴地宣布&#xff0c;ChunJun 即将迎来 1.16 Release 版本的正式发布。在新版本中&#xff0c;ChunJun 新增了一批常用功能&#xff0c;进行了多项功能优化和问题修复&#xff0c;并在用户使用体验上进行了极大地改…

systemctl 启动/停止/重新加载 nginx

systemctl 启动/停止/重新加载 nginx 一、新建nginx.service脚本 sudo vim /usr/lib/systemd/system/nginx.service然后按iii进入编辑模式&#xff0c;粘贴如下内容&#xff0c;其中/usr/local/nginx/是进行make && make install之后的文件夹路径&#xff0c;需要根据…

虚拟地址空间

本节目录1.如何理解区域划分2.为什么一个变量可以存储两个不同的值&#xff1f;3.深入理解虚拟地址空间为什么要有地址空间&#xff1f;4.理解什么是挂起&#xff1f;1.虚拟地址空间究竟是什么&#xff1f;2.映射关系的维护是谁做的&#xff1f;1.如何理解区域划分 所谓的区域…

STM32F1,F4,L1系列禁止JTAG和SW引脚方法

STM32F1系列 程序中在使用到JTAG、SWD的某个IO 时&#xff0c;需要禁用掉相关调试方法后&#xff0c;再配置相应的IO方式。在需要相应的接口配置前使用这些代码。 对于F1系列&#xff0c;调用函数进行专门的禁止。 标准库配置方式&#xff1a; RCC_APB2PeriphClockCmd(RCC_A…

【蓝桥杯嵌入式】LCD屏的原理图解析与代码实现(第十三届省赛为例)——STM32

&#x1f38a;【蓝桥杯嵌入式】专题正在持续更新中&#xff0c;原理图解析✨&#xff0c;各模块分析✨以及历年真题讲解✨都在这儿哦&#xff0c;欢迎大家前往订阅本专题&#xff0c;获取更多详细信息哦&#x1f38f;&#x1f38f;&#x1f38f; &#x1fa94;本系列专栏 - 蓝…

智能家居Homekit系列一智能触摸开关

触摸开关&#xff0c;即通过触摸方式控制的墙壁开关&#xff0c;其感官场景如同我们的触屏手机&#xff0c;只需手指轻轻一点即可达到控制电器的目的&#xff0c;随着人们生活品质的提高&#xff0c;触摸开关将逐渐将换代传统机械按键开关。 触摸开关控制原理 触摸开关我们把…

HNU工训中心: 三人表决器及八人抢答器实验报告

工训中心的牛马实验 三人表决器&#xff1a; 实验目的 1) 辨识数字IC功能说明。 2) 测试数字集成门电路&#xff0c;掌握输出故障排除、使用注意事项。 3) 掌握逻辑函数搭建三人表决器。 2.实验资源 HBE硬件基础电路实验箱、万用表 74LS00与非门、74LS10 三个3输入与非门…

JS 的同步异步、事件循环

js 执行过程&#xff1a;先执行同步代码在执行异步代码案例1: 以下就是 同步代码异步代码&#xff0c;同步代码先执行&#xff0c;异步代码后执行console.log(1); //1 setTimeout(()>{ console.log(3) //2 },10)案例2: 执行顺序console.log(1) //1 setTimeout(()>{ conso…

ChatGTP--事务是否生效?

项目场景&#xff1a; 由于最近在做一个项目的cola代码改造,所以由于cola需要在 service 和 dao层中间加一层>>> gateway,和springcloud中的gateway不一样,这个仅仅是抽象了一层; 思考: 现在我的事务 依然在service中开启,那么我的事务经过了一个gateway后,再次调用d…

死锁/资源分配/银行家算法

一、死锁 1、原因 竞争资源/程序推进顺序不当 2、必要条件 1、互斥条件 2、请求和保持条件 3、不剥夺条件 4、环路等待条件 3、处理死锁基本方法 1、预防死锁(摒弃除1以外的条件) 2、避免死锁(银行家算法) 3、检测死锁(资源分配图) …

机器学习:图神经网络graph neural network

图神经网络 graph neural networks Neural network Graph GNN应用 关系是否可以提供一些额外信息&#xff0c;不止是考虑entity的特征&#xff0c;还可以考虑关系。 基于近朱者赤 近墨者黑的假设 用邻居更新利用信号系统中&#xff0c;先转到频域&#xff0c;再做基于频域的…

I - 太阳轰炸(组合数学Cnk n固定)

2023河南省赛组队训练赛&#xff08;二&#xff09; - Virtual Judge (vjudge.net) 背景&#xff1a;阿塔尼斯&#xff0c;达拉姆的大主教&#xff0c;在艾尔又一次沦陷之后指挥着星灵的最后一艘方舟舰&#xff1a;亚顿之矛。作为艾尔星灵数千年来的智慧结晶&#xff0c;亚顿之…

数据分享|ESA_WorldCover_10m_2020: 2020年的10米土地覆盖数据(WorldCover v100)

2021年10月,欧洲航空局(ESA)发布了2020年的10米土地覆盖数据(WorldCover v100),该数据基于哨兵1号和哨兵2号的数据,包含11个土地覆盖类别,整体精度达到了74.4%,目前在公开可获取的同类型数据产品里具有一定的优势。那么这份数据具体是什么样子,又该如何获取呢?让我们…