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

JAVA通过AOP自定义注解记录日志

JAVA通过AOP自定义注解记录日志

  • 背景
  • 一、自定义注解
  • 二、定义一个切面
  • 三、记录日志的实体类
  • 四、使用该注解

背景

需求:系统的操作日志、审计日志。在日常的管理还是维护中都会起到很大的作用。

解决办法:可以在需要的方法中对日志进行保存操作,但是对业务代码入侵性大。
或者使用切面针对控制类进行处理,但是灵活度不高。因此决定使用自定义注解 + 切面来针对方法进行日志记录。

目前日志主要记录的有三方面:

  • 请求的入参,出参
  • 关于业务上的操作
  • 异常日常日志的打印

一、自定义注解

创建自定义注解 @AuditLog

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AuditLog {// 1.操作描述String action() default "";// 2.操作类型(增删改查)OperateEnum type() default OperateEnum.MODIFY;// 3.是否记录参数boolean isRecord() default true;
}

操作类型枚举类:新增、删除、修改、查询。

public enum OperateEnum {ADD,DELETE,MODIFY,SAVE_OR_MODIFY,SELECT;
}

二、定义一个切面

创建一个切面类AuditAspect ,用于捕获带有@AuditLog 注解的方法调用并记录日志:

import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;/*** 日志记录切面*/
@Slf4j
@Aspect
@Component
public class AuditAspect {@Pointcut(value = "@annotation(com.bocloud.devops.eaas.api.log.AuditLog)")public void logMethodPointCut() {}@Around("logMethodPointCut()")public Object recordSysLog(ProceedingJoinPoint point) throws Throwable {UserLogRecordDO userLogRecordDO = bulidRequestParams(point);//先执行业务try {Object result = point.proceed();if (result != null) {userLogRecordDO.setResultMsg(JSONUtil.toJsonStr(result));}return result;} catch (BusinessException e) {userLogRecordDO.setResultCode(e.getCode());userLogRecordDO.setResultMsg(e.getMessage());throw e;} catch (Exception e) {userLogRecordDO.setResultCode("");userLogRecordDO.setResultMsg("后端未知异常");throw e;} finally {//操作日志入库try {//XXXService.save(userLogRecordDO);} catch (Exception e) {log.warn("记录用户操作异常:{}", e.getMessage());}}}/*** 记录方法和方法入参* @param point* @return*/protected UserLogRecordDO bulidRequestParams(ProceedingJoinPoint point) {MethodSignature methodSignature = (MethodSignature) point.getSignature();Method method = methodSignature.getMethod();AuditLog annotation = method.getAnnotation(AuditLog.class);UserLogRecordDO userLogRecordDO = new UserLogRecordDO();userLogRecordDO.setAction(annotation.action());userLogRecordDO.setType(annotation.type().name());userLogRecordDO.setMethodName(method.getDeclaringClass().getSimpleName() + "." + method.getName());try {// 处理入参Parameter[] parameters = methodSignature.getMethod().getParameters();HashMap<String, Object> paramMap = new HashMap<>();Object[] args = point.getArgs();for (int i = 0; i < parameters.length; i++) {AuditLog auditLog = parameters[i].getAnnotation(AuditLog.class);if (auditLog != null) {continue;}Class<?> type = parameters[i].getType();if (ServletResponse.class.isAssignableFrom(type) || ServletRequest.class.isAssignableFrom(type)) {continue;}String name = parameters[i].getName();paramMap.put(name, args[i]);}userLogRecordDO.setMethodParams(JSONUtil.toJsonStr(paramMap));return userLogRecordDO;} catch (Exception e) {log.warn("构建入参异常:{}", e.getMessage());}return userLogRecordDO;}
}
  • logMethodPointCut(): 这个方法定义了一个切入点(pointcut),它使用@annotation注解来匹配带有com.bocloud.devops.eaas.api.log.AuditLog注解的方法。这意味着切面将在这些被标记为@AuditLog的方法执行前后生效。

  • recordSysLog(ProceedingJoinPoint point): 这是一个环绕通知方法,用于包围切入点方法的执行。它负责记录系统日志和审计信息。具体的操作包括:

    • 构建请求参数并创建一个UserLogRecordDO对象,其中包括操作描述、操作类型、方法名等信息。
    • 调用point.proceed()来执行切入点方法,捕获方法的执行结果,将结果信息记录到UserLogRecordDO中。
    • 处理可能抛出的BusinessException异常,记录异常信息。
    • 在finally块中尝试将操作日志入库,但在此示例中,入库的部分被注释掉。
  • bulidRequestParams(ProceedingJoinPoint point): 这个方法用于构建方法的参数信息,并将其记录到UserLogRecordDO对象中。它包括方法名、操作描述、操作类型以及方法的入参信息。此方法通过反射分析方法参数和参数上的AuditLog注解,以构建参数信息的JSON表示。

此切面的主要目的是在标记了@AuditLog注解的方法执行前后记录操作日志信息。在实际应用中,您需要确保数据库操作以及记录日志的部分(在finally块中)按照实际需求进行配置。此示例代码中的入库部分被注释掉,您需要根据自己的需求实现相应的数据持久化逻辑。

三、记录日志的实体类

import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import lombok.Data;/*** @program: eaas-center* @description:* @author: yanchao* @create: 2023-10-10 23:09**/
@Data
@ApiOperation("操作日志记录表")
public class UserLogRecordDO {@ApiModelProperty("主键")private String id;@ApiModelProperty("行为描述")private String action;@ApiModelProperty("执行方法")private String methodName;@ApiModelProperty("执行入参")private String methodParams;@ApiModelProperty("操作类型")private String type;@ApiModelProperty("响应编码")private String resultCode;@ApiModelProperty("结果描述")private String resultMsg;}

四、使用该注解

    /*** 通过id查询中心维护表** @param id 主键* @return 单条数据*/@GetMapping("{id}")@AuditLog(action = "通过id查询中心维护表", type = OperateEnum.SELECT)public Result<EaasCentralMaintainQueryDetailRes> detail(@PathVariable("id") Integer id{return Result.success(this.eaasCentralMaintainService.detail(id));}

这样即可在数据库查看相应的操作日志记录。


http://www.mrgr.cn/news/65285.html

相关文章:

  • CentOS 7 安装 ntp,自动校准系统时间
  • arkUI:文本框、文本域的创建和常见用法(TextInput 、TextArea)
  • 幼儿园篮球游戏
  • 深度学习相关资料
  • 全志A133 android10 LVDS幅值调节
  • el-table 滚动条重置 手动控制滚动条
  • 100种算法【Python版】第38篇—— Tarjan算法
  • 智能推荐系统介绍
  • 【人工智能-初级】练习题:matplotlib基础练习30例
  • Python 中的迭代器与生成器详解
  • 关于halcon的可变形logo模板匹配find_local_deformable_modle_xld解释及简化匹配代码
  • JavaScript函数
  • 物联网赋能的人工智能图像检测系统
  • 探索 Python 的新天地:Helium 库揭秘
  • 代码随想录训练营Day15 | 530.二叉搜索树的最小绝对差 - 501.二叉搜索树中的众数 - 236. 二叉树的最近公共祖先
  • 15.函数的重载
  • 04741计算机网络原理真题-CRC的计算-案例分析
  • PHP+MySQL开发的一套招聘管理系统开发案例源码功能介绍
  • H5页面在线预览pdf
  • 照明灯十大知名品牌有哪些?2024灯具十大公认品牌排行榜出炉!
  • SpringMVC课时2
  • FFmpeg 4.3 音视频-多路H265监控录放C++开发十. 多线程控制帧率。循环播放,QT connect 细节,
  • SpringBoot新闻稿件管理系统:开发与实践
  • 亚马逊营销邮件:高效策略提升邮件转化率!
  • 前端项目配置文件的各种配置
  • STM32HAL-最简单的长、短、多击按键框架(多按键)