zoukankan      html  css  js  c++  java
  • 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

    相关内容链接跳转:

    切面编程注解操作日志

  • 相关阅读:
    mysqldump 导出数据库为DBname的表名为Tname的表结构 导出数据库的所有表的表结构
    mysqldump 备份某张表 Warning: A partial dump from a server that has GTIDs will by default include the GTIDs of all transactions,
    nfs missing codepage or helper program, or other error
    date 增加一个小时 减少一个小时
    mysqldump 备份单个数据库
    mysql删除账户
    怎么删除某个用户的所有帖子?
    mongodb删除重复数据
    ReSharper2018破解详细方法
    激活windows和office
  • 原文地址:https://www.cnblogs.com/buzheng/p/14101189.html
Copyright © 2011-2022 走看看