我们如何实现业务操作日志功能?_系统日志功能实现-程序员宅基地

技术标签: java  安全  后台  服务器  

1. 需求

我们经常会有这样的需求,需要对关键的业务功能做操作日志记录,也就是用户在指定的时间操作了哪个功能,操作前后的数据记录,必要的时候可以一键回退,今天我就为大家实现这个的功能,让大家可以直接拿来用。

1.1 日志分类

  1. 系统日志:指的是程序执行过程中的关键步骤记录,根据实际输出的debug、info、warn、error等级别的执行记录信息,主要用来记录核心参数以及返回值,同时为了在出现问题的时候能够快速排查问题
  2. 操作日志:它是用户实际业务操作行为的记录,主要是为了分析用户的行为偏好,一方面用来对业务进行分析,开发人员也可以针对访问的频率去提高特定接口的性能。

2. 设计思路

首先我们得要分析具体要实现这个功能,都需要记录哪些信息,然后怎么要去实现这个功能?
具体功能点:

  1. 记录用户的业务操作行为,具体字段有:操作人、操作时间、操作功能、日志类型、操作内容描述、操作内容、操作前内容。
  2. 需要对外提供页面和管理功能,以便查询追溯和数据回滚。

我们首先要想到,要实现这个功能,最好的办法就是需要和业务逻辑进行解耦,因为它是一种业务辅助功能,同时是对所有业务的一种横向操作,那我们使用什么技术,是不是就呼之欲出了?最容易想到的就是AOP切面+自定义注解

2.1 实现步骤

1、首先定义操作日志注解,注解内定义一些属性,如操作功能名称、描述等;

2、将自定义注解标记在需要进行业务操作记录的方法上,一般查询不需要。

3、定义切入点,编写切面:切入点就是标记业务操作日志注解的目标方法;切面就是保存业务操作日志信息。

对了,在这里我想给大家说一下咱们经常用到的几个技术的区别,过滤器,拦截器,SpringAOP,这个也是我经常的困惑。

2.2 SpringAOP、过滤器、拦截器对比

在匹配中同一目标时,过滤器、拦截器、SpringAOP的执行优先级是:过滤器>拦截器>SpringAOP,执行顺序是先进后出,主要有如下区别:

  1. 过滤器(Filter)
    过滤器,就是起到过滤筛选作用的一种事物,只不过这里的过滤器过滤的对象是客户端访问的web资源。也可以理解为一种预处理手段,对资源进行拦截后,将其中我们认为的杂质(用户自己定义的)过滤,符合条件的放行,不符合的则拦截下来。
    过滤器常见的使用场景:统一设置编码,过滤敏感字符,登录校验,URL级别的访问权限控制,数据压缩。
    在这里插入图片描述
    2.拦截器(Interceptor)
    拦截器是springmvc提供的,类似于过滤器。主要用于拦截用户请求并作相应的处理。
    拦截器的使用场景: 日志记录,权限校验,登录校验,性能检测,经常会用在网关处。
    在这里插入图片描述
    3:AOP
    AOP拦截的是类的元数据(包、类、方法名、参数等),AOP针对具体的代码,能够实现更加复杂的业务逻辑。
    使用的场景:日志记录,性能统计,安全控制,事务处理,异常处理。

3. 具体实现

3.1 引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

3.2 数据库表设计

create table if not exists bus_log
(
   id bigint auto_increment comment '自增id'
      primary key,
   bus_name varchar(100) null comment '业务名称',
   bus_descrip varchar(255) null comment '业务操作描述',
   oper_person varchar(100) null comment '操作人',
   oper_time datetime null comment '操作时间',
   ip_from varchar(50) null comment '操作来源ip',
   param_file varchar(255) null comment '操作参数报文文件'
)
comment '业务操作日志' default charset ='utf8';

3.3 代码实现

  1. 定义日志注解
/**
 * 业务日志注解
 * 可以作用在控制器或其他业务类上,用于描述当前类的功能;
 * 也可以用于方法上,用于描述当前方法的作用;
 */
@Target({
    ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface BusLog {
    
 
 
    /**
     * 功能名称
     * @return
     */
    String name() default "";
 
    /**
     * 功能描述
     * @return
     */
    String descrip() default "";
 
}
  1. 编写切面

主要步骤是:在环绕通知内执行过目标方法后,获取目标类、目标方法上的业务日志注解上的功能名称和功能描述, 把方法的参数报文写入到文件中,最后保存业务操作日志信息;

@Component
@Aspect
@Slf4j
public class BusLogAop implements Ordered {
    
    @Autowired
    private BusLogDao busLogDao;
 
    /**
     * 定义BusLogAop的切入点为标记@BusLog注解的方法
     */
    @Pointcut(value = "@annotation(com.wuk.BusLog)")
    public void pointcut() {
    
    }
 
    /**
     * 业务操作环绕通知
     */
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) {
    
        log.info("----BusAop 环绕通知 start");
        //执行目标方法
        Object result = null;
        try {
    
            result = proceedingJoinPoint.proceed();
        } catch (Throwable throwable) {
    
            throwable.printStackTrace();
        }
        //目标方法执行完成后,获取目标类、目标方法上的业务日志注解上的功能名称和功能描述
        Object target = proceedingJoinPoint.getTarget();
        Object[] args = proceedingJoinPoint.getArgs();
        MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
        BusLog anno1 = target.getClass().getAnnotation(BusLog.class);
        BusLog anno2 = signature.getMethod().getAnnotation(BusLog.class);
        BusLogBean busLogBean = new BusLogBean();
        String logName = anno1.name();
        String logDescrip = anno2.descrip();
        busLogBean.setBusName(logName);
        busLogBean.setBusDescrip(logDescrip);
        busLogBean.setOperPerson("wuk");
        busLogBean.setOperTime(new Date());
        JsonMapper jsonMapper = new JsonMapper();
        String json = null;
        try {
    
            json = jsonMapper.writeValueAsString(args);
        } catch (JsonProcessingException e) {
    
            e.printStackTrace();
        }
        //把参数报文写入到文件中
        OutputStream outputStream = null;
        try {
    
            String paramFilePath = System.getProperty("user.dir") + File.separator + DateUtil.format(new Date(), DatePattern.PURE_DATETIME_MS_PATTERN) + ".log";
            outputStream = new FileOutputStream(paramFilePath);
            outputStream.write(json.getBytes(StandardCharsets.UTF_8));
            busLogBean.setParamFile(paramFilePath);
        } catch (FileNotFoundException e) {
    
            e.printStackTrace();
        } catch (IOException e) {
    
            e.printStackTrace();
        } finally {
    
            if (outputStream != null) {
    
                try {
    
                    outputStream.flush();
                    outputStream.close();
                } catch (IOException e) {
    
                    e.printStackTrace();
                }
 
            }
        }
        //保存业务操作日志信息
        this.busLogDao.insert(busLogBean);
        log.info("----BusAop 环绕通知 end");
        return result;
    }
 
    @Override
    public int getOrder() {
    
        return 1;
    }
}
  1. 业务接口添加注解
@RestController
@Slf4j
@RequestMapping("/person")
public class PersonController {
    
    @Autowired
    private IPersonService personService;
 
    @PostMapping
    @BusLog(name="添加人员信息",descrip = "添加人员信息")
    public Person add(@RequestBody Person person) {
    
        Person result = this.personService.registe(person);
        log.info("//增加person执行完成");
        return result;
    }
    
    @PutMapping
    @BusLog(name="修改人员信息",descrip = "修改人员信息")
    public void edit(@RequestBody Person person) {
    
         this.personService.update(person);
    }
    
    @DeleteMapping
    @BusLog(name="删除人员信息", descrip = "删除人员信息")
    public void delete(@PathVariable(name = "id") Integer id) {
    
         this.personService.delete(id);
    }
}
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/wu2374633583/article/details/131219500

智能推荐

揭开均线系统的神秘面纱_揭开极限编程的神秘面纱,重新探讨“ XP蒸馏”,第2部分...-程序员宅基地

文章浏览阅读496次。存档日期:2019年5月15日 | 首次发布:2002年9月10日 在本月的Demystifying Extreme Programming中 ,Roy Miller解释了成为XP团队的程序员意味着什么,以及六个程序员的实践如何特别适合这种情况。 虽然所有19种XP实践都很重要,但程序员实践对于团队开发软件至关重要 。 此内容不再被更新或维护。 全文以PDF格式“按原样”提供。 随着技..._解开均线系统的神秘面纱

centos7.5开启ssh服务-程序员宅基地

文章浏览阅读5k次,点赞2次,收藏15次。1,查看是否已安装openssh-server;指令:rpm -qa | grep ssh2,如果未安装,请先安装openssh-serveryum install openssh-server3,编辑ssh参数:vim /etc/ssh/sshd_config修改端口号允许root登录设置需要密码登录修改好后输入“:wqa”保存退出4,开启ss..._centos7.5开启ssh

【2020-11-02】分享一个爬虫小工具,提高你的工作效率_trillworks-程序员宅基地

文章浏览阅读1.2k次,点赞4次,收藏7次。文章目录前言二、地址https://curl.trillworks.com/三、用法3.1这里以有道翻译为例,右键链接>copy>copy as curl(bash)3.2 黏贴到上面的网站,右侧方框会自动填充requests请求总结前言我们在编写爬虫代码的时候,都需要去请求链接,有事还需要一连串的参数,这时候我们是不可能一个一个手打上去的,这次就分享一个快速解析请求的小工具二、地址https://curl.trillworks.com/三、用法3.1这里以有道翻译为例,右键._trillworks

C++——多项式拟合_c++ 多项式拟合-程序员宅基地

文章浏览阅读1.5w次,点赞16次,收藏88次。 C++——多项式拟合目标:利用C++对txt或者xml中的数据,进行高阶或低阶多项式拟合 为方便以后查找,代码以及详细资料已打包,并上传至云盘(链接:https://pan.baidu.com/s/1bvUBIoxv7Avxeq_Cz6xOZQ 密码:u9qe)打包的内容如下: ..._c++ 多项式拟合

农产品:菜鸟、京东、顺丰们争夺冷链物流的新战场?-程序员宅基地

文章浏览阅读204次。互联网时代,一切都有了新玩法。以吃为例,从古至今中国人对“吃”的热爱都没有变过,在现今如何吃的健康、营养为国人新追求,有机蔬菜、无农药水果成为时下新宠。同时,随着国内物流体系的完善,运输技术的提高,农产品正成为新风口。就在今年3月1日,由国家发改委牵头、24部委联合发布的《关于推动物流高质量发展促进形成强大国内市场的意见》中明确指出,要加强农产品物流骨干网络和冷链物流体系建设,发展如“生鲜电..._顺丰冷链物流基础设施落后

Android Studio项目结构详解_andriodstudio的构建在哪-程序员宅基地

文章浏览阅读6.8k次,点赞8次,收藏67次。Android Studio 是谷歌推出的一个Android集成开发工具,基于IntelliJ IDEA. 类似 Eclipse ADT,Android Studio 提供了集成的 Android 开发工具用于开发和调试。在IDEA的基础上,Android Studio 提供: 1.基于Gradle的构建支持。 2.Android 专属的重构和快速修复。 3. 提示工具以捕获性能、可用性、版本兼容性等问题。 4.支持ProGuard和应用签名。 5.基于模板的向导来生成常用的Android_andriodstudio的构建在哪

随便推点

吃透一切整流滤波电路_高频正弦波整流纹波尖峰-程序员宅基地

文章浏览阅读3.2w次,点赞94次,收藏554次。基础电路一般直流稳压电源都使用220伏市电作为电源,经过变压、整流、滤波后输送给稳压电路进行稳压,最终成为稳定的直流电源。这个过程中的变压、整流、滤波等电路可以看作直流稳压电源的基础电路,没有这些电路对市电的前期处理,稳压电路将无法正常工作。1、变压电路通常直流稳压电源使用电源变压器来改变输入到后级电路的电压。电源变压器由初级绕组、次级绕组和铁芯组成。初级绕组用来输入电源交流电压,次级绕组输出所需..._高频正弦波整流纹波尖峰

Android性能优化工具_android 性能优化工具-程序员宅基地

文章浏览阅读1.4k次,点赞2次,收藏7次。CPU 性能分析器可以在APP运行时,实时检查应用的 CPU 使用率和线程活动,也可以检查记录的方法轨迹、函数轨迹和系统轨迹的详情。(1)Sample JAVA Methods不能采集耗时很短的方法,而Trace JAVA Methods可以采集耗时很短的方法;_android 性能优化工具

大学实训_软件毕设_Java入门实战_商场管理系统_Punrain_java为超市设计管理系统-程序员宅基地

文章浏览阅读5.2k次,点赞8次,收藏41次。需要 演示文档+数据库涉及+需求分析文档和完整源码的,可通过该链接下载https://download.csdn.net/download/Punrain/11980745_java为超市设计管理系统

Runtime-iOS运行时应用篇_initwithframe runtime拦截-程序员宅基地

文章浏览阅读151次。在上篇文章iOS运行时Runtime基础后,本篇将会总结Rutime的具体应用实例,结合其动态特性,Runtime在开发中的应用大致分为以下几个方面:Runtime应用.png相关文章:iOS运行时Runtime基础一、动态方法交换:Method Swizzling实现动态方法交换(Method Swizzling )是Runtime中最具盛名的应用场景,其原理是:通过Runtime获取到方法实现的地址,进而动态交换两个方法的功能。使用到关键方法如下://获取类方法的M.._initwithframe runtime拦截

2020-08-14_20200814_112325010.jpg-程序员宅基地

文章浏览阅读65次。SpringMvc解决跨域问题前言:弄的一个Mvc项目涉及到了跨域问题,以下是我整理的一些解决方法介绍: 跨站 HTTP 请求(Cross-site HTTP request)是指发起请求的资源所在域不同于该请求所指向资源所在的域的 HTTP 请求。比如说,域名A(http://xusong.example)的某 Web 应用程序中通过标签引入了域名B(http://xusong.foo)站点的某图片资源(http://xusong.foo/image.jpg),域名A的那 Web 应用会使浏览器发_20200814_112325010.jpg

阿里云不同账户下的ecs服务器迁移教程_阿里云两个账户的服务器能不能做镜像迁移-程序员宅基地

文章浏览阅读565次。服务器已购买并创建系统盘的情况下只能同步系统盘,数据盘无法同步。系统盘还未创建的可以同步系统盘和数据盘。4、镜像共享给你要迁移的阿里云服务器的账号上,不是登录账号,是账号ID(个人中心->安全设置->账号ID)3、使用最新的快照生成镜像,镜像是分区域的,华南一只能用华南一的镜像。4、在接收镜像的账号里查看镜像,注意选择正确的区域。2、要迁移的实例创建镜像,创建镜像基于快照。5、创建实例,选择镜像,还是要注意区域。1、先停机维护要迁移的服务器。6、选择共享镜像,更换即可。5、新服务器更换系统盘。_阿里云两个账户的服务器能不能做镜像迁移

推荐文章

热门文章

相关标签