自定义注解(二)——系统日志记录

news2024/11/24 22:46:11

        在上一篇自定义注解(一)——统一请求拦截中对自定义注解做了简单说明及关于统一token认证的应用示例。其实对于自定义注解,还有一种常用的方法是用于系统日志记录,此处的系统日志记录,区别于@Slf4j或@Log4j把日志文件写入到log文件中,而是直接写入到数据库表中。在AOP切面中可以跟踪入参情况、异常情况、返回值情况,并且把这些关键信息全部持久化到数据库中。以下是示例代码及说明:

文章目录

    • 1. 自定义Log注解
    • 2. 新建日志记录表
    • 3. AOP上定义日志切面方法
      • 3-1 处理完成的请求
      • 3-2 拦截异常操作
      • 3-3 核心逻辑处理
      • 3-4 其他辅助方法
    • 4. 方法上应用
      • 4-1 应用场景
      • 4-2 传参记录操作名称
    • 5. 总结

1. 自定义Log注解

自定义注解中传入name的参数,用于标记方法中的模块名称

/**
 * 日志记录自定义注解
 */
@Retention(RetentionPolicy.RUNTIME) // 注解运行在哪一个时期的
@Target({ ElementType.PARAMETER, ElementType.METHOD }) // 注解用在哪上边
@Documented
public @interface Log {

    /** 模块名称 */
    public String name() default "";

}

2. 新建日志记录表

日志记录可以按照实际需求进行建表,模块名称在注解中可以作为传参传入,方法名称、请求方式等都可以通过程序获取到,重点记录请求参数、返回值、操作人员和操作时间。

在这里插入图片描述

按照数据库表建立映射实体,对应实体如下:

@Data
public class SysOperLog {
	/** 主键ID */
    private Long operId;
    /** 模块名称 */
    private String name;
    /** 方法名称 */
    private String method;
    /** 请求方式 */
    private String requestMethod;
    /** 请求URL */
    private String operUrl;
    /** 操作人员ID */
    private Long operUserId;
    /** 请求参数 */
    private String operParam;
    /** 操作状态 */
    private Integer status;
    /** 错误消息 */
    private String errorMsg;
    /** 操作时间 */
    private Date operTime;
}

3. AOP上定义日志切面方法

定义LogAspect类用于AOP处理日志逻辑,分别加上@Aspect(AOP管理)、@Component(类放入Spring容器中)、@Slf4j(日志注解)3个注解

3-1 处理完成的请求

需要传入aspectj下的JoinPoint接口,用于正常业务逻辑日志记录

	/**
     * 处理完请求后执行
     */
    @AfterReturning(pointcut = "@annotation(sysLog)", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Log sysLog, Object jsonResult) {
        handleLog(joinPoint, sysLog, null, jsonResult);
    }

3-2 拦截异常操作

需要传入aspectj下的JoinPoint接口,用于try…catch…之外的异常抛出日志记录

	/**
     * 拦截异常操作
     */
    @AfterThrowing(value = "@annotation(sysLog)", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Log sysLog, Exception e) {
        handleLog(joinPoint, sysLog, e, null);
    }

3-3 核心逻辑处理

入参包括:aspectj下的JoinPoint接口、日志记录自定义注解Log、异常类Exception、返回值jsonResult。在方法中设置方法名称、请求方式、参数、操作时间、返回信息等,并通过线程的方式存入到数据库中。

	protected void handleLog(final JoinPoint joinPoint, Log sysLog, final Exception e, Object jsonResult) {
        try {
            // 操作日志处理
            SysOperLog operLog = new SysOperLog();
            operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
            operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255));

            if (e != null) {
                // 方法中出现异常信息
                operLog.setStatus(BusinessStatus.FAIL.ordinal());
                operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
            } else {
                R r = (R)jsonResult;
                if (r.getCode() != 0) {
                    // 方法中未出现异常信息,但是执行错误
                    operLog.setStatus(r.getCode());
                    operLog.setErrorMsg(r.getMsg());
                }
            }
            // 设置方法名称
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            operLog.setMethod(className + "." + methodName + "()");
            // 设置请求方式
            operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
            // 设置名称
            operLog.setName(sysLog.name());
            // 获取参数的信息,传入到数据库中。
            setRequestValue(joinPoint, operLog);
            // 设置操作人
            Long userId = getUserIdByToken(); //TODO 根据token信息获取操作人ID
            operLog.setOperUserId(userId);
            // 设置操作时间
            operLog.setOperTime(new Date());
            // 使用线程池,保存数据库 TODO
            log.info("待保存日志:"+JSON.toJSONString(operLog));
        } catch (Exception exp) {
            // 记录本地异常日志
            log.error("==前置通知异常==");
            log.error("异常信息:{}", exp.getMessage());
            exp.printStackTrace();
        }
    }

3-4 其他辅助方法

/**
     * 获取请求的参数,放到log中
     */
    private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog) throws Exception {
        String requestMethod = operLog.getRequestMethod();
        if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) {
            String params = argsArrayToString(joinPoint.getArgs());
            operLog.setOperParam(StringUtils.substring(params, 0, 2000));
        } else {
            Map<?, ?> paramsMap = (Map<?, ?>) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
            operLog.setOperParam(StringUtils.substring(paramsMap.toString(), 0, 2000));
        }
    }

    /**
     * 参数拼装
     */
    private String argsArrayToString(Object[] paramsArray) {
        String params = "";
        if (paramsArray != null && paramsArray.length > 0) {
            for (Object o : paramsArray) {
                if (StringUtils.isNotNull(o)) {
                    try {
                        String jsonObj = JSON.toJSONString(o);
                        params += jsonObj.toString() + " ";
                    } catch (Exception e) {
                    }
                }
            }
        }
        return params.trim();
    }
    
	public enum BusinessStatus {
        SUCCESS,
        FAIL,
    }

4. 方法上应用

4-1 应用场景

一般应用于增、删、改的方法中,查询方法可以不使用(因为查询的数据量比较大,重点记录敏感操作日志)

4-2 传参记录操作名称

在注解括号中传入参数信息,此处的key值与Log注解中的属性保持一致,如下图:

在这里插入图片描述

5. 总结

        通过自定义Log注解,并且加在需要记录日志表的方法上,就可以满足关键方法的日志记录。大大提升了我们的工作效率和代码整洁度,便于后期调试优化。对于错误方法,主要分为两块,一块是抛出异常之后执行了doAfterThrowing的方法,另一块是程序中做好了try…catch…的异常处理机制,执行doAfterReturning方法时,对 code!=0 的返回结果记录。除此之外,区别与上一篇的Token注解,Token注解的切面是在方法执行之前,Log注解的切面是在方法执行之后!

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

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

相关文章

Big Data and Cognitive Computing (IF=3.7) 期刊投稿

Special Issue: Artificial Cognitive Systems for Computer Vision 欢迎计算机视觉相关工作的投稿&#xff01; 影响因子3.7&#xff0c;截止时间2024年12月31日 投稿咨询&#xff1a;lqyan18fudan.edu.cn 投稿网址&#xff1a;https://www.mdpi.com/journal/BDCC/special_iss…

模板方法模式:定义算法骨架的设计策略

在软件开发中&#xff0c;模板方法模式是一种行为型设计模式&#xff0c;它在父类中定义一个操作的算法框架&#xff0c;允许子类在不改变算法结构的情况下重定义算法的某些步骤。这种模式是基于继承的基本原则&#xff0c;通过抽象类达到代码复用的目的。本文将详细介绍模板方…

【算法刷题 | 回溯思想 02】4.12(电话号码的字母组合)

文章目录 4.电话号码的字母组合4.1问题4.2解法&#xff1a;回溯4.2.1回溯思路&#xff08;1&#xff09;函数返回值以及参数&#xff08;2&#xff09;终止条件&#xff08;3&#xff09;遍历过程 4.2.2代码实现 4.电话号码的字母组合 4.1问题 给定一个仅包含数字 2-9 的字符…

阻塞队列和生产消费模型

阻塞队列 阻塞队列的概念 队列相信我们已经不陌生了 之前也学过很多队列 比如: 普通队列 和 优先级队列 两种 这两种队列都是线程不安全的 而我们讲的阻塞队列 刚好可以解决线程安全问题 也是先进先出 并且带有阻塞功能. 阻塞功能是怎么回事呢 就是如果入队的时候阻塞队列为…

AI的智商如何测试?

一、AI的智商测试 AI的智商测试不同于人类的智商测试&#xff0c;因为它涉及到不同类型的智能和功能衡量标准。以下是几种常见的AI智商测试或评估方式&#xff1a; 此图片来源于网络 1. **功能性测试**&#xff1a; - 对于算法或机器学习模型&#xff0c;可以通过标准化的基…

探秘大模型:《提示工程:技巧、方法与行业应用》背后的故事

提示工程是一种新兴的利用人工智能的技术&#xff0c;它通过设计提示引导生成式 AI 模型产生预期的输出&#xff0c;来提升人与 AI 的互动质量&#xff0c;激发 AI 模型的潜力&#xff0c;提升AI的应用水平。 为了让每一个人都拥有驱动大模型的能力&#xff0c;以微软全球副总裁…

maven之pom中的build标签

1、build标签分类 1.1、全局配置&#xff08;project build&#xff09; 针对整个项目的所有情况都有效。 <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation"htt…

Linux执行命令监控详细实现原理和使用教程,以及相关工具的使用

Linux执行命令监控详细实现原理和使用教程&#xff0c;以及相关工具的使用。 0x00 背景介绍 Linux上的HIDS需要实时对执行的命令进行监控&#xff0c;分析异常或入侵行为&#xff0c;有助于安全事件的发现和预防。为了获取执行命令&#xff0c;大致有如下方法&#xff1a; 遍…

OJ 古籍翻译 【进制转化】【C】

这里有两个考察点&#xff0c;一个是进制转换&#xff0c;还有一个是以字符串输出 知识点&#xff1a; sprintf(参数一&#xff0c;参数二&#xff0c;参数三) 将参数三按参数二的格式转换后存入参数一中 sprintf与printf用法基本一样&#xff0c;只是sprintf是将值打印到指…

R语言数据可视化:基本绘图系统

目录 plot函数 par函数 hist函数 boxplot函数 plot函数应用实战 全局参数 R语言中有三大绘图系统包括基本绘图系统&#xff0c;Lattice绘图系统&#xff0c;ggplot2绘图系统 基本绘图系统 在R语言中&#xff0c;以下函数通常用于创建和定制图形&#xff1a; plot 函数…

ansible的常见用法

目录 ##编辑hosts文件 ##copy模块##复制过去 ##fetch模块##拉取 ##shell模块 ##好用 ##command模块## ##file模块### ##cron模块### ##crontab 计划任务 ##下载好时间插件 ##script模块 ##yum模块## ##yum下载源配置文件 /etc/yum.repos.d/CentOS-Base.repo ##ser…

时间序列分析 # 平稳性检验和ARMA模型的识别与定阶 #R语言

掌握单位根检验的原理并能解读结果&#xff1b;掌握利用序列的自相关图和偏自相关图识别模型并进行初步定阶。 原始数据在文末&#xff01;&#xff01;&#xff01; 练习1、根据某1971年9月-1993年6月澳大利亚季度常住人口变动&#xff08;单位&#xff1a;千人&#xff09;的…

MySQL 嵌套查询

嵌套查询 是指在一个完整的查询语句之中&#xff0c;包含若干个不同功能的小查询&#xff1b;从而一起完成复杂查询的一种编写形式。包含的查询放在&#xff08;&#xff09;里 &#xff0c; 包含的查询出现的位置&#xff1a; 位置含义SELECT之后把查询结果作为表头使用FROM…

ubuntu20.04.3挂载共享文件夹

VMware设置win共享文件夹 在linux执行挂载命令 sudo vmhgfs-fuse /mnt/hgfs/ -o nonempty这样就可以用管理员访问/mnt/hgfs/share

场景:如何做数据清理

如果数据清理简单粗暴按时间进行清理&#xff0c;同时时间字段并没有增加索引就会出问题 如果没有增加索引&#xff0c;他就会进行全表扫描&#xff0c;并且会给全表的数据上一个x锁 会阻塞其他的线程 解决方案参考阿里云DMS数据清理方案 这个SQL查询的目的是从名为table_hol…

2024年mathorcup数学建模思路及论文助攻

题目C题 物流网络分拣中心货量预测及人员排班 电商物流网络在订单履约中由多个环节组成&#xff0c;图1是一个简化的物流网络示意图。其中&#xff0c;分拣中心作为网络的中间环节&#xff0c;需要将包裹按照不同流向进行分拣并发往下一个场地&#xff0c;最终使包裹到达消费者…

Towards Street-Level Client-Independent IP Geolocation(2011年)(第一部分)

被引次数:306 Wang Y, Burgener D, Flores M, et al. Towards {Street-Level}{Client-Independent}{IP} Geolocation[C]//8th USENIX Symposium on Networked Systems Design and Implementation (NSDI 11). 2011. Abstract 一个高度精确的客户端独立的地理定位服务将是互联…

电商广告中的OCPC是什么?OCPC原理是什么?

1.定义&#xff1a; OCPC是指optimized cost per click&#xff08;以目标转化为优化方式的点击出价&#xff09;&#xff0c;本质还是按照cpc付费。 2.三大特点&#xff1a;抢流量、避免无效流量、提高转化&#xff1b; 3.放量原理&#xff1a; 通过捕捉用户行为、行业数据…

数组算法——查询位置

需求 思路 使用二分查找找到第一个值&#xff0c;以第一个值作为界限&#xff0c;分为左右两个区间在左右两个区间分别使用二分查找找左边的7,&#xff1a;找到中间位置的7之后&#xff0c;将中间位置的7作为结束位置&#xff0c;依次循环查找&#xff0c;知道start>end,返回…

股票价格预测 | Python使用LSTM预测股票价格

文章目录 效果一览文章概述代码设计效果一览 文章概述 Python使用LSTM预测股票价格 代码设计 import pandas as pd import matplotlib.pyplot as plt import numpy as np import tensorflowfrom numpy import