当前位置: 首页 > news >正文

后端——AOP异步日志

需求分析

在SpringBoot系统中,一般会对访问系统的请求做日志记录的需求,确保系统的安全维护以及查看接口的调用情况,可以使用AOP对controller层的接口进行增强,作日志记录。日志保存在数据库当中,为了避免影响接口的响应,降低用户体验度,采用异步的方式记录日志,避免日志记录阻塞接口请求

实现原理

通过定义AOP切面,访问接口之前,使用前置通知记录一些有用的数据,如ip地址、请求方式、操作人等等,接口执行完之后使用后置通知记录本次请求执行的实践、执行结果等等信息。
在这里插入图片描述

实现代码

AOP日志切面

定义切点表达式指向Controller的所有方法,即指向所有接口

/**
 * @Description 日志切面类
 */
@Aspect
@Component
public class SysLogAspect {

    @Autowired
    SysLogDao sysLogDao;

    /**
     * 开始时间
     */
    private Long startTime;

    /**
     * ip
     */
    private String ip;

    /**
     * 请求接口名
     */
    private String interfaceName;

    /**
     * 请求方式
     */
    private String methodWays;

    /**
     * 请求方法路径
     */
    private String requestMethodUrl;

    /**
     * 请求类方法参数
     */
    private String requestArgs;

    //声明切点
    @Pointcut("execution(public * com.*.*.controller.*.*(..)) || execution(public * com.*.controller.*.*(..))"
    private void aspect() {
    }

    @Before("aspect()")
    public void before(JoinPoint joinPoint) throws NoSuchMethodException {
        //开始访问的时间
        startTime = System.currentTimeMillis();
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        //获取请求ip
        ip = request.getRemoteAddr();
        //获取请求方法名
        interfaceName = request.getRequestURI();
        //获取请求方式
        methodWays = request.getMethod();
        //获取请求方法地址
        requestMethodUrl = joinPoint.getSignature().toString();
        //获取请求参数
        requestArgs = Arrays.toString(joinPoint.getArgs());
    }

    @AfterReturning(pointcut = "aspect()")
    public void methodAfterReturning() {
        Long endTime = System.currentTimeMillis();
        //执行时长
        Double time = (endTime - startTime) / 1000.0;
        SysLog sysLog = new SysLog();
        //获取当前用户
        Subject currentUser = SecurityUtils.getSubject();
        UserLoginInfo subject = (UserLoginInfo) currentUser.getPrincipal();
        if (!ObjectUtils.isEmpty(subject)) {
            //用户名
            sysLog.setUserName(subject.getUserName());
        }
        //用户操作
        switch (methodWays) {
            case "GET":
                sysLog.setOperation(BusinessType.SELECT.getValue());
                break;
            case "POST":
                sysLog.setOperation(BusinessType.INSERT.getValue());
                break;
            case "PUT":
                sysLog.setOperation(BusinessType.UPDATE.getValue());
                break;
            case "DELETE":
                sysLog.setOperation(BusinessType.DELETE.getValue());
                break;
        }
        //请求IP
        sysLog.setIp(ip);
        //请求类方法参数
        sysLog.setParams(requestArgs);
        //执行时长
        sysLog.setTime(time.toString());
        //请求方法路径
        sysLog.setMethod(requestMethodUrl);
        //入库时间
        String createTime = DateTimeUtils.getNowDateTime();
        sysLog.setCreateTime(createTime);
        //获取系统接口路径信息列表
        List<ApiInfo> apiInfos = sysLogDao.selectApiInfos();
        for (ApiInfo apiInfo : apiInfos) {
            //调用接口路径与接口信息列表进行匹配
            if (apiInfo.getApiName().equals(interfaceName)) {
                //菜单模块
                sysLog.setMenuModel(apiInfo.getMenuOperation());
                break;
            }
        }
        //异步新增日志
        AsyncManager.me().execute(AsyncFactory.recordOper(sysLog));
    }
}

异步任务管理器(线程池)

定义一个单例的异步任务管理器,使用线程池完成异步操作

/**
 * @description: 异步任务管理器
 **/
public class AsyncManager {
    /**
     * 操作延迟10毫秒
     */
    private final int OPERATE_DELAY_TIME = 10;

    /**
     * 异步操作任务调度线程池
     */
    private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService");

    /**
     * 单例模式
     */
    private AsyncManager(){}

    private static AsyncManager me = new AsyncManager();

    public static AsyncManager me()
    {
        return me;
    }

    /**
     * 执行任务
     *
     * @param task 任务
     */
    public void execute(TimerTask task)
    {
        executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);
    }

    /**
     * 停止任务线程池
     */
    public void shutdown()
    {
        ThreadsUtil.shutdownAndAwaitTermination(executor);
    }
}

异步工厂

这里定义异步工厂去创建日记记录的任务,同时也方便系统扩展其他的异步操作

public class AsyncFactory {

    /**
     * 操作日志记录
     *
     * @param operLog 操作日志信息
     * @return 任务task
     */
    public static TimerTask recordOper(final SysLog operLog) {
        return new TimerTask() {
            @Override
            public void run() {
                // 远程查询操作地点
                SpringUtils.getBean(SysLogService.class).addSysLogInfo(operLog);
            }
        };
    }

}

相关文章:

  • 伊吖学C笔记(2、文件、启动、数学基础)
  • Python——成员变量
  • C语言入门教程100讲(40)文件定位
  • 大文件版本管理git-lfs
  • Zookeeper运维指南:服务端与客户端常用命令详解
  • Chrome Performance 面板完全指南:从卡顿到丝滑的终极调试术
  • 一站式开源AI平台Cherry Studio本地部署与远程调用本地大模型
  • AI知识补全(二):提示工程(Prompting)是什么?
  • 自定义minshell
  • Python----计算机视觉处理(Opencv:模板匹配)
  • 价值流映射(Value Stream Mapping):从流程可视化到敏捷效能革命
  • 【深度学习】【目标检测】【OnnxRuntime】【C++】YOLOV3模型部署
  • 深度解析:打破知识孤岛,降低重复开发成本(5大重点)
  • conda常用指令
  • 【基础】Windows 中通过 VSCode 使用 GCC 编译调试 C++
  • 【QA】为什么gl_Posision必须被赋值?
  • 人工智能笔记
  • 开源模型应用落地-语音转文本-whisper模型-AIGC应用探索(四)
  • 【QA】OpenGL的渲染流程是怎么样的?
  • 提升TikTok直播流量的有效方法分析
  • 云南铁路:打造“铁路+金融+产业”融合发展生态
  • 江西省公安厅警务保障部原主任辛卫平主动交代问题,正接受审查调查
  • 王毅会见泰国外长玛里:坚决有力打击电诈等跨境犯罪
  • 武汉一季度GDP为4759.41亿元,同比增长5.4%
  • 气温“过山车”现象未来或更频繁且更剧烈
  • “90后”高层建筑返青春:功能调整的技术路径和运营考验