• springboot通过切面编程实现系统请求操作日志记录


    1、引入依赖包

     <!-- aop 依赖包 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>1.8.6</version>
    </dependency>
    
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.25</version>
    </dependency>
    
     <dependency>
         <groupId>cn.hutool</groupId>
         <artifactId>hutool-all</artifactId>
         <version>5.3.8</version>
    </dependency>
    View Code

    2、相应数据库

    系统操作日志表

    CREATE TABLE `sys_log_operation` (
      `id` varchar(32) NOT NULL COMMENT 'id',
      `request_uri` varchar(200) DEFAULT NULL COMMENT '请求URI',
      `request_method` varchar(20) DEFAULT NULL COMMENT '请求方式',
      `class_method` varchar(200) DEFAULT NULL COMMENT '请求函数',
      `request_params` text COMMENT '请求参数',
      `request_time` bigint DEFAULT NULL COMMENT '请求时长(毫秒)',
      `user_agent` varchar(500) DEFAULT NULL COMMENT '用户代理',
      `request_ip` varchar(32) DEFAULT NULL COMMENT '操作ip',
      `state` int DEFAULT NULL COMMENT '状态(0、失败,1、成功)',
      `create_time` datetime NOT NULL COMMENT '创建时间',
      `create_user` varchar(32) DEFAULT NULL COMMENT '创建人',
      `creator_name` varchar(32) DEFAULT NULL COMMENT '创建人名',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='系统操作日志';
    View Code

    系统错误日志表

    CREATE TABLE `sys_log_error` (
      `id` varchar(32) NOT NULL COMMENT 'id',
      `request_uri` varchar(200) DEFAULT NULL COMMENT '请求URI',
      `request_method` varchar(20) DEFAULT NULL COMMENT '请求方式',
      `class_method` varchar(200) DEFAULT NULL COMMENT '请求函数',
      `request_params` text COMMENT '请求参数',
      `request_time` int DEFAULT NULL COMMENT '请求时长(毫秒)',
      `user_agent` varchar(500) DEFAULT NULL COMMENT '用户代理',
      `request_ip` varchar(32) DEFAULT NULL COMMENT '操作ip',
      `error_info` text COMMENT '异常信息',
      `create_time` datetime NOT NULL COMMENT '创建时间',
      `create_user` varchar(32) DEFAULT NULL COMMENT '创建人',
      `creator_name` varchar(32) DEFAULT NULL COMMENT '创建人名',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='系统错误日志';
    View Code

    3、日志实体类

    package com.bz.bean;
    
    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableField;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import io.swagger.annotations.ApiModel;
    import io.swagger.annotations.ApiModelProperty;
    import java.io.Serializable;
    import java.util.Date;
    import lombok.Data;
    
    /**
     * 异常日志
     */
    @Data
    @TableName(value = "sys_log_error")
    public class SysLogError implements Serializable {
        /**
         * id
         */
        @TableId(value = "id", type = IdType.INPUT)
        @ApiModelProperty(value = "id")
        private String id;
    
        /**
         * 请求URI
         */
        @TableField(value = "request_uri")
        @ApiModelProperty(value = "请求URI")
        private String requestUri;
    
        /**
         * 请求方式
         */
        @TableField(value = "request_method")
        @ApiModelProperty(value = "请求方式")
        private String requestMethod;
    
        /**
         * 请求参数
         */
        @TableField(value = "request_params")
        @ApiModelProperty(value = "请求参数")
        private String requestParams;
    
        /**
         * 用户代理
         */
        @TableField(value = "user_agent")
        @ApiModelProperty(value = "用户代理")
        private String userAgent;
    
        /**
         * 操作IP
         */
        @TableField(value = "ip")
        @ApiModelProperty(value = "操作IP")
        private String ip;
    
        /**
         * 异常信息
         */
        @TableField(value = "error_info")
        @ApiModelProperty(value = "异常信息")
        private String errorInfo;
    
       /**
         * 创建人
         */
        @TableField(value = "create_user")
        @ApiModelProperty(value = "创建人")
        private String createUser;
    
        /**
         * 创建人名
         */
        @TableField(value = "creator_name")
        @ApiModelProperty(value = "创建人名")
        private String creatorName;
    
        /**
         * 创建时间
         */
        @TableField(value = "create_date")
        @ApiModelProperty(value = "创建时间")
        private Date createDate;
    
        /**
         * 类方法
         */
        @TableField(value = "class_method")
        @ApiModelProperty(value = "类方法")
        private String classMethod;
    
        private static final long serialVersionUID = 1L;
    }
    
    
    package com.enzenith.homemaking.entity;
    
    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableField;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import io.swagger.annotations.ApiModel;
    import io.swagger.annotations.ApiModelProperty;
    import java.io.Serializable;
    import java.util.Date;
    import lombok.AllArgsConstructor;
    import lombok.Builder;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    /**
     * 系统操作日志
     */
    @Data
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    @TableName(value = "sys_log_operation")
    public class SysLogOperation implements Serializable {
        /**
         * id
         */
        @TableId(value = "id", type = IdType.INPUT)
        @ApiModelProperty(value = "id")
        private String id;
    
        /**
         * 请求URI
         */
        @TableField(value = "request_uri")
        @ApiModelProperty(value = "请求URI")
        private String requestUri;
    
        /**
         * 请求方式
         */
        @TableField(value = "request_method")
        @ApiModelProperty(value = "请求方式")
        private String requestMethod;
    
        /**
         * 请求函数
         */
        @TableField(value = "class_method")
        @ApiModelProperty(value = "请求函数")
        private String classMethod;
    
        /**
         * 请求参数
         */
        @TableField(value = "request_params")
        @ApiModelProperty(value = "请求参数")
        private String requestParams;
    
        /**
         * 请求时长(毫秒)
         */
        @TableField(value = "request_time")
        @ApiModelProperty(value = "请求时长(毫秒)")
        private Long requestTime;
    
        /**
         * 用户代理
         */
        @TableField(value = "user_agent")
        @ApiModelProperty(value = "用户代理")
        private String userAgent;
    
        /**
         * 操作ip
         */
        @TableField(value = "request_ip")
        @ApiModelProperty(value = "操作ip")
        private String requestIp;
    
        /**
         * 状态(0、失败,1、成功)
         */
        @TableField(value = "state")
        @ApiModelProperty(value = "状态(0、失败,1、成功)")
        private Integer state;
    
        /**
         * 创建时间
         */
        @TableField(value = "create_time")
        @ApiModelProperty(value = "创建时间")
        private Date createTime;
    
        /**
         * 创建人
         */
        @TableField(value = "create_user")
        @ApiModelProperty(value = "创建人")
        private String createUser;
    
        /**
         * 创建人名
         */
        @TableField(value = "creator_name")
        @ApiModelProperty(value = "创建人名")
        private String creatorName;
    
        /**
         * 备注(用于数据迁移)
         */
        @TableField(value = "memo")
        @ApiModelProperty(value = "备注(用于数据迁移)")
        private String memo;
    
        private static final long serialVersionUID = 1L;
    }
    View Code

    4、切面实现类

    package com.bz.aspect;
    
    import cn.hutool.core.util.IdUtil;
    import cn.hutool.core.util.StrUtil;
    import cn.hutool.json.JSONUtil;
    import com.alibaba.fastjson.JSON;
    import com.bz.bean.SysLogError;
    import com.bz.bean.SysLogOperation;
    import com.bz.common.enums.OperationStatusEnum;
    import com.bz.service.SysLogErrorService;
    import com.bz.service.SysLogOperationService;
    import org.apache.commons.lang.StringEscapeUtils;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.AfterThrowing;
    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.json.JSONObject;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    import org.springframework.web.multipart.MultipartFile;
    
    import javax.servlet.http.HttpServletRequest;
    import java.lang.reflect.Method;
    import java.util.Date;
    
    /**
     * 切面日志
     * @author Buzheng
     */
    @Aspect
    @Component
    public class RequestLogAspect {
        private final static Logger LOGGER = LoggerFactory.getLogger(RequestLogAspect.class);
    
        @Autowired(required = false)
        private SysLogOperationService sysLogOperationService;
    
        @Autowired(required = false)
        private SysLogErrorService sysLogErrorService;
    
        /**
         * 定义切入点
         */
        @Pointcut("execution(* com.bz.api..*.*(..))")
        public void requestServer() {
        }
    
        /**
         * 环绕通知
         * 既可以在目标方法之前织入增强动作,也可以在执行目标方法之后织入增强动作;
         * 可以决定目标方法在什么时候执行,如何执行,甚至可以完全阻止目标目标方法的执行;
         * 可以改变执行目标方法的参数值,也可以改变执行目标方法之后的返回值; 当需要改变目标方法的返回值时,只能使用Around方法;
         */
        @Around("requestServer()")
        public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            //获取系统时间
            long start = System.currentTimeMillis();
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            //获取请求信息
            HttpServletRequest request = attributes.getRequest();
            Object result = proceedingJoinPoint.proceed();
            //保存录入数据库
            SysLogOperation requestInfo = new SysLogOperation();
            requestInfo.setId(IdUtil.createSnowflake(1,1).nextId());
            //获取客户端的IP地址
            requestInfo.setRequestIp(request.getRemoteAddr());
            //获取请求的接口地址
            requestInfo.setRequestUri(request.getRequestURL().toString());
            requestInfo.setRequestMethod(request.getMethod());
            //获取请求哪个类以及哪个方法
            requestInfo.setClassMethod(String.format("%s.%s", proceedingJoinPoint.getSignature().getDeclaringTypeName(),
                    proceedingJoinPoint.getSignature().getName()));
            //获取请求参数
            requestInfo.setRequestParams(getRequestParamsByProceedingJoinPoint(proceedingJoinPoint));
            requestInfo.setState(OperationStatusEnum.SUCCESS.value());
            requestInfo.setRequestTime(System.currentTimeMillis() - start);
            requestInfo.setCreateTime(new Date());
            /**
             *  判断是否有token,因为登录的时候是没有的。所以需要进行判断
             *  ps:前端每次请求接口时都会带token进行请求。
             */
            String token = request.getHeader("token");
            if(StrUtil.isNotBlank(token)){
                requestInfo.setCreatorName("获取请求者名称");
                requestInfo.setCreateUser("获取请求者用户id");
            }
            sysLogOperationService.save(requestInfo);
    
            return result;
        }
    
        /**
         * 异常通知:目标方法抛出异常时执行
         */
        @AfterThrowing(pointcut = "requestServer()", throwing = "e")
        public void doAfterThrow(JoinPoint joinPoint, RuntimeException e) {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
    
            SysLogError requestErrorInfo = new SysLogError();
    
            requestErrorInfo.setId(String.valueOf(IdUtil.createSnowflake(1,1).nextId()));
            requestErrorInfo.setIp(request.getRemoteAddr());
            requestErrorInfo.setRequestUri(request.getRequestURL().toString());
            requestErrorInfo.setRequestMethod(request.getMethod());
            requestErrorInfo.setClassMethod(String.format("%s.%s", joinPoint.getSignature().getDeclaringTypeName(),
                    joinPoint.getSignature().getName()));
            requestErrorInfo.setRequestParams(getRequestParamsByJoinPoint(joinPoint));
            //获取报错信息
            requestErrorInfo.setErrorInfo(e.getMessage());
            requestErrorInfo.setCreateDate(new Date());
            /**
             *  判断是否有token,因为登录的时候是没有的。所以需要进行判断
             *  ps:前端每次请求接口时都会带token进行请求。
             */
            String token = request.getHeader("token");
            if(StrUtil.isNotBlank(token)){
                requestErrorInfo.setCreatorName("获取请求者名称");
                requestErrorInfo.setCreateUser("获取请求者用户id");
            }
            boolean save = sysLogErrorService.save(requestErrorInfo);
            LOGGER.info("Error Request Info      : {}", JSON.toJSONString(requestErrorInfo));
        }
    
        /**
         * 获取入参
         * @param proceedingJoinPoint
         * */
        private String getRequestParamsByProceedingJoinPoint(ProceedingJoinPoint proceedingJoinPoint) {
            //参数名
            String[] paramNames = ((MethodSignature)proceedingJoinPoint.getSignature()).getParameterNames();
            //参数值
            Object[] paramValues = proceedingJoinPoint.getArgs();
            //去除反斜杠
            return   StringEscapeUtils.unescapeJavaScript(buildRequestParam(paramNames, paramValues).toString());
        }
    
         /**
         * 获取入参
         * @param proceedingJoinPoint
         * */
        private String getRequestParamsByJoinPoint(JoinPoint joinPoint) {
            //参数名
            String[] paramNames = ((MethodSignature)joinPoint.getSignature()).getParameterNames();
            //参数值
            Object[] paramValues = joinPoint.getArgs();
            return  StringEscapeUtils.unescapeJavaScript(buildRequestParam(paramNames, paramValues).toString());
        }
    
        /**
         * 将参数名称以及参数值转成json字符串
         * @param paramNames    参数名称
         * @param paramValues   参数值
         * @return cn.hutool.json.JSONObject
         * @author Buzheng
         **/
        private JSONObject buildRequestParam(String[] paramNames, Object[] paramValues) {
            JSONObject jsonObject = new JSONObject();
            for (int i = 0; i < paramNames.length; i++) {
                Object value = paramValues[i];
                //如果是文件对象
                if (value instanceof MultipartFile) {
                    MultipartFile file = (MultipartFile) value;
                    value = file.getOriginalFilename();  //获取文件名
                }
                jsonObject.putOpt(paramNames[i], value != null ? JSONUtil.toJsonStr(value) : null);
            }
            return jsonObject;
        }
    }
    View Code

    相关内容链接跳转:

    切面编程注解操作日志

  • 相关阅读:
    Django----抽屉项目 笔记
    Django验证码【附源码】
    C语言--循环控制结构
    javascript 流程控制及函数
    yield和yield from
    python面试题(转)
    断言assert用法
    javascript基本语法
    python经典一百道习题(转自奶酪博客)
    functools模块中的函数
  • 原文地址:https://www.cnblogs.com/buzheng/p/14101189.html
Copyright © 2020-2023  润新知