• AOP应用之系统操作日志


    本文演示下如何使用AOP,去实现系统操作日志功能。

    实现步骤

    1. 引入AOP包
    <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-aop</artifactId>
         <version>2.6.6</version>
     </dependency>
    
    1. 定义数据模型
    package com.angel.ocean.domain.entity;
    
    import lombok.Data;
    import java.util.Date;
    
    @Data
    public class Syslog {
        /**
         * 主键
         **/
        private Long id;
        /**
         * ip 地址
         **/
        private String ip;
        /**
         * 类名
         **/
        private String className;
        /**
         * 方法名称
         **/
        private String methodName;
        /**
         * 传参
         **/
        private String params;
        /**
         * 执行结果, true-成功,false-失败
         **/
        private boolean status;
        /**
         * 响应信息
         **/
        private String response;
        /**
         * 业务类型
         **/
        private String remark;
        /**
         * 触发时间
         **/
        private Date createTime;
        /**
         * 操作用户
         **/
        private String createBy;
    }
    
    1. 定义系统日志注解SyslogAnno
    package com.angel.ocean.annotation;
    
    import java.lang.annotation.*;
    
    /**
     * 定义系统日志注解
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface SyslogAnno {
    
        String value() default "";
    
    }
    
    1. 定义切面SyslogAspect
    package com.angel.ocean.aspect;
    
    import com.alibaba.fastjson2.JSON;
    import com.angel.ocean.annotation.SyslogAnno;
    import com.angel.ocean.domain.entity.Syslog;
    import com.angel.ocean.runner.SyslogHandlerTask;
    import com.angel.ocean.util.LogDataUtil;
    import lombok.extern.slf4j.Slf4j;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.stereotype.Component;
    import java.lang.reflect.Method;
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    
    /**
     1. 日志切面
     */
    @Slf4j
    @Aspect
    @Component
    public class SyslogAspect {
    
        @Pointcut("@annotation(com.angel.ocean.annotation.SyslogAnno)")
        public void logPointCut() {
        }
    
        @AfterReturning(value = "logPointCut()", returning = "result")
        public void normalLog(JoinPoint point, Object result) {
            try {
                Syslog syslog = getSysLog(point);
                syslog.setStatus(true);
                syslog.setResponse(JSON.toJSONString(result));
                SyslogHandlerTask.LOG_QUEUE.offer(syslog);
            } catch (Exception e) {
                log.error("SyslogAspect.normalLog() error. ", e);
            }
        }
    
        @AfterThrowing(value = "logPointCut()", throwing = "throwable")
        public void exceptionLog(JoinPoint point, Throwable throwable) {
            try {
                Syslog syslog = getSysLog(point);
                syslog.setStatus(false);
                syslog.setResponse(throwable.getMessage());
                SyslogHandlerTask.LOG_QUEUE.offer(syslog);
            } catch (Exception e) {
                log.error("SyslogAspect.exceptionLog() error. ", e);
            }
        }
    
        private Syslog getSysLog(JoinPoint joinPoint) {
    
            Syslog sysLog = new Syslog();
    
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
    
            SyslogAnno sysLogAnno = method.getAnnotation(SyslogAnno.class);
            if (sysLogAnno != null) {
                sysLog.setRemark(sysLogAnno.value());
            }
    
            // 请求的 类名、方法名
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = signature.getName();
            sysLog.setClassName(className);
            sysLog.setMethodName(methodName);
    
            // 请求的参数
            Object[] args = joinPoint.getArgs();
            try {
                List<String> list = new ArrayList<>();
                for (Object o : args) {
                    list.add(JSON.toJSONString(o));
                }
                sysLog.setParams(list.toString());
            } catch (Exception e) {
                log.error("SyslogAspect.saveLog() error.", e);
            }
    
            sysLog.setIp(LogDataUtil.getIP());
            sysLog.setCreateBy(LogDataUtil.getUserId());
            sysLog.setCreateTime(new Date());
    
            return sysLog;
        }
    }
    

    工具类

    package com.angel.ocean.util;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    import javax.servlet.http.HttpServletRequest;
    
    @Slf4j
    public class LogDataUtil {
    
        private LogDataUtil() {}
    
        /**
         *  获取用户IP地址
         */
        public static String getIP() {
            String ip = "";
            try {
                HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
                ip = request.getRemoteAddr();
            } catch (Exception e) {
                log.error("GetIPException", e);
            }
            return ip;
        }
    
        /**
         * 获取用户ID
         */
        public static String getUserId() {
            String userId = "";
            try {
                HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
                userId = request.getHeader("userId");
            } catch (Exception e) {
                log.error("GetUserIdException", e);
            }
            return userId;
        }
    }
    
    1. 定义日志数据处理SyslogHandlerTask
    package com.angel.ocean.runner;
    
    import cn.hutool.core.collection.CollUtil;
    import com.angel.ocean.domain.entity.Syslog;
    import com.angel.ocean.service.SyslogService;
    import com.google.common.collect.Queues;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.boot.CommandLineRunner;
    import org.springframework.stereotype.Component;
    import javax.annotation.Resource;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.BlockingQueue;
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.TimeUnit;
    
    @Slf4j
    @Component
    public class SyslogHandlerTask implements CommandLineRunner {
    
        @Resource
        private SyslogService syslogService;
    
        /**
         * 操作日志队列
         */
        public static final BlockingQueue<Syslog> LOG_QUEUE = new LinkedBlockingQueue<>();
    
        @Override
        public void run(String... strings) throws Exception {
            new Thread(() -> {
                List<Syslog> SyslogList = new ArrayList<>();
                while (true) {
                    try {
                        Queues.drain(LOG_QUEUE, SyslogList, 100, 500, TimeUnit.MILLISECONDS);
                        if (CollUtil.isNotEmpty(SyslogList)) {
                            syslogService.batchInsert(SyslogList);
                            SyslogList.clear();
                        } else {
                            Thread.sleep(500);
                        }
                    } catch (Exception e) {
                        log.error("SyslogHandlerTask error={}", e.getMessage(), e);
                    }
                }
            }, "Syslog_Props_Mysql").start();
        }
    }
    
    1. 打包成系统操作日志SDK (ocean-log)

    如何使用

    继承系统操作日志SDK

    <dependency>
        <groupId>com.angel.ocean</groupId>
        <artifactId>ocean-log</artifactId>
        <version>1.0.0</version>
    </dependency>
    

    在Api的添加/修改/删除等方法上,添加SyslogAnno注解

    @ApiModelProperty(value = "保存角色信息表")
    @PostMapping("save")
    @SyslogAnno("添加角色")
    public ApiResult<?> save(@RequestBody SysRoleDTO dto) {
        service.save(dto);
        return ApiResult.success();
    }
    

    实例验证

    接口调用
    在这里插入图片描述系统操作日志数据
    在这里插入图片描述

  • 相关阅读:
    Python适合0基础菜鸟学吗
    [安卓android毕业设计]精品基于Uniapp+SSM实现的校园心理健康APP[源码]
    Python 三维姿态估计+Unity3d 实现 3D 虚拟现实交互游戏
    Vue(四)——使用脚手架(1)
    .NET C#基础(6):命名空间 - 有名字的作用域
    https网址大部分电脑没问题,部分就是提示下面的各种试试
    Promise面试实战指北
    STC8H开发(十三): I2C驱动DS3231高精度实时时钟芯片
    获取图片中的图表数据并拟合函数
    为什么大厂压力大,竞争大,还有这么多人热衷于大厂呢?
  • 原文地址:https://blog.csdn.net/m0_37978198/article/details/139882928