springBoot AOP记录操作日志和异常日志

springBoot AOP记录操作日志和异常日志

1.创建日志表

-- ----------------------------
-- Table structure for sys_log
-- ----------------------------
DROP TABLE IF EXISTS "public"."sys_log";
CREATE TABLE "public"."sys_log" (
  "id" varchar(64) COLLATE "pg_catalog"."default" NOT NULL,
  "thread" int4,
  "ip" varchar(30) COLLATE "pg_catalog"."default",
  "uri" varchar(100) COLLATE "pg_catalog"."default",
  "operate_module" varchar(100) COLLATE "pg_catalog"."default",
  "operate_type" varchar(50) COLLATE "pg_catalog"."default",
  "operate_method" varchar(100) COLLATE "pg_catalog"."default",
  "operate_desc" varchar(500) COLLATE "pg_catalog"."default",
  "request_state" int4,
  "request_param" varchar(500) COLLATE "pg_catalog"."default",
  "response_param" varchar(1000) COLLATE "pg_catalog"."default",
  "exception_name" varchar(255) COLLATE "pg_catalog"."default",
  "exception_message" text COLLATE "pg_catalog"."default",
  "create_time" timestamp(6),
  "user_id" varchar(50) COLLATE "pg_catalog"."default",
  "user_name" varchar(255) COLLATE "pg_catalog"."default",
  "session_time" int4
)
;
COMMENT ON COLUMN "public"."sys_log"."id" IS 'id';
COMMENT ON COLUMN "public"."sys_log"."thread" IS '线程id';
COMMENT ON COLUMN "public"."sys_log"."ip" IS 'ip';
COMMENT ON COLUMN "public"."sys_log"."uri" IS '请求连接';
COMMENT ON COLUMN "public"."sys_log"."operate_module" IS '功能模块';
COMMENT ON COLUMN "public"."sys_log"."operate_type" IS '操作类型';
COMMENT ON COLUMN "public"."sys_log"."operate_method" IS '操作方法';
COMMENT ON COLUMN "public"."sys_log"."operate_desc" IS '操作描述';
COMMENT ON COLUMN "public"."sys_log"."request_state" IS '请求状态(1正常 2异常)';
COMMENT ON COLUMN "public"."sys_log"."request_param" IS '请求参数';
COMMENT ON COLUMN "public"."sys_log"."response_param" IS '返回参数';
COMMENT ON COLUMN "public"."sys_log"."exception_name" IS '异常名称';
COMMENT ON COLUMN "public"."sys_log"."exception_message" IS '异常信息';
COMMENT ON COLUMN "public"."sys_log"."create_time" IS '操作时间';
COMMENT ON COLUMN "public"."sys_log"."user_id" IS '用户id';
COMMENT ON COLUMN "public"."sys_log"."user_name" IS '用户名';
COMMENT ON COLUMN "public"."sys_log"."session_time" IS '请求时长(秒)';

-- ----------------------------
-- Primary Key structure for table sys_log
-- ----------------------------
ALTER TABLE "public"."sys_log" ADD CONSTRAINT "sys_log_pkey" PRIMARY KEY ("id");

2. 添加maven依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

3.创建操作日志注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperateLog {

    // 操作模块
    String operateModul() default "";

    // 操作类型
    String operateType() default "";

    // 操作说明
    String operateDesc() default "";
}

4.对应数据库的实体类

@Data
@TableName("sys_log")
public class SysLog implements Serializable {

    private static final long serialVersionUID = 115492983341579610L;

    /** id **/
    @TableId(value = "id",type = IdType.ASSIGN_UUID)
    private String id;

    /** 线程id **/
    private Integer thread;

    /** ip **/
    private String ip;

    /** 请求连接 **/
    private String uri;

    /** 功能模块 **/
    private String operateModule;

    /** 操作类型 **/
    private String operateType;

    /** 操作方法 **/
    private String operateMethod;

    /** 操作描述 **/
    private String operateDesc;

    /** 请求状态(1正常 2异常) **/
    private Integer requestState;

    /** 请求参数 **/
    private String requestParam;

    /** 返回参数 **/
    private String responseParam;

    /** 异常名称 **/
    private String exceptionName;

    /** 异常信息 **/
    private String exceptionMessage;

    /** 操作时间 **/
    private Date createTime;

    /** 用户id **/
    private String userId;

    /** 用户名 **/
    private String userName;

    /** 请求时长(秒) **/
    private int sessionTime;
}

5.创建切面类来记录日志

@Aspect
@Component
public class OperateLogAspect {

    @Autowired
    private SysLogMapper sysLogMapper;

    private static ThreadLocal<Long> startTime = new ThreadLocal<Long>();

    // 设置操作日志切入点 记录操作日志 在注解的位置切入代码
    @Pointcut("@annotation(com.gisquest.entity.OperateLog)")
    public void operatePointCut() {
    }


    /**
     * 设置操作异常切入点记录异常日志 扫描所有controller包下操作
     */
    @Pointcut("execution(* com.gisquest.controller..*.*(..))")
    public void operateExceptionPoinCut() {
    }


    /**
     * 前置通知
     * 获取开始的毫秒值
     */
    @Before("operatePointCut()||operateExceptionPoinCut()")
    public void doBefore() {
        startTime.set(System.currentTimeMillis());
    }

    /**
     * 返回通知
     * 拦截用户操作日志,连接点正常执行完成后执行, 如果连接点抛出异常,则不会执行
     *
     * @param joinPoint 切入点
     * @param keys      返回结果
     */
    @AfterReturning(value = "operatePointCut()", returning = "keys")
    public void saveOperLog(JoinPoint joinPoint, Object keys) {
        saveLogInfo(joinPoint, keys, null);
    }

    /**
     * 异常返回通知,用于拦截异常日志信息 连接点抛出异常后执行
     *
     * @param joinPoint 切入点
     * @param e         异常信息
     */
    @AfterThrowing(pointcut = "operateExceptionPoinCut()", throwing = "e")
    public void saveExceptionLog(JoinPoint joinPoint, Throwable e) {
        saveLogInfo(joinPoint, null, e);
    }


    /**
     * 保存日志信息
     *
     * @param joinPoint 切点
     * @param keys      返回值
     * @param e         异常类
     */
    public void saveLogInfo(JoinPoint joinPoint, Object keys, Throwable e) {
        // 获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) requestAttributes
                .resolveReference(RequestAttributes.REFERENCE_REQUEST);
        SysLog sysLog = new SysLog();
        try {
            // 从切面织入点处通过反射机制获取织入点处的方法
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            // 获取切入点所在的方法
            Method method = signature.getMethod();
            // 获取请求的类名
            String className = joinPoint.getTarget().getClass().getName();
            // 获取请求的方法名
            String methodName = method.getName();
            methodName = className + "." + methodName;
            sysLog.setOperateMethod(methodName); // 请求方法
            // 请求的参数
            Map<String, String> rtnMap = converMap(request.getParameterMap());
            // 将参数所在的数组转换成json
            String params = JSON.toJSONString(rtnMap);

            // 获取操作
            OperateLog opLog = method.getAnnotation(OperateLog.class);
            if (opLog != null) {
                String operModul = opLog.operateModul();
                String operType = opLog.operateType();
                String operDesc = opLog.operateDesc();
                sysLog.setOperateModule(operModul);
                sysLog.setOperateType(operType);
                sysLog.setOperateDesc(operDesc);
            }

            if (e != null) {
                // 异常名称
                sysLog.setExceptionName(e.getClass().getName());
                // 异常信息
                sysLog.setExceptionMessage(stackTraceToString(e.getClass().getName(), e.getMessage(), e.getStackTrace()));
            }

            //获取线程id
            int threadId = (int) Thread.currentThread().getId();
            sysLog.setThread(threadId);
            sysLog.setRequestParam(params);
            sysLog.setResponseParam(JSON.toJSONString(keys));
            sysLog.setUserId(UserUtils.userId());    // 这边自己公司的工具类 
            sysLog.setUserName(UserUtils.nickname());// 你们可把用户信息放在Request中获取
            sysLog.setIp(getIpAddressa(request));
            sysLog.setUri(request.getRequestURI());
            sysLog.setCreateTime(new Date());
            sysLog.setRequestState(e == null ? 1 : 2);
            sysLog.setSessionTime((int) ((System.currentTimeMillis() - startTime.get()) / 1000));
            //插入数据到数据库
            sysLogMapper.insert(sysLog);
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }

    /**
     * 转换request 请求参数
     *
     * @param paramMap request获取的参数数组
     */
    public Map<String, String> converMap(Map<String, String[]> paramMap) {
        Map<String, String> rtnMap = new HashMap<String, String>();
        for (String key : paramMap.keySet()) {
            rtnMap.put(key, paramMap.get(key)[0]);
        }
        return rtnMap;
    }

    /**
     * 转换异常信息为字符串
     *
     * @param exceptionName    异常名称
     * @param exceptionMessage 异常信息
     * @param elements         堆栈信息
     */
    public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) {
        StringBuffer strbuff = new StringBuffer();
        for (StackTraceElement stet : elements) {
            strbuff.append(stet + "\n");
        }
        String message = exceptionName + ":" + exceptionMessage + "\n\t" + strbuff.toString();
        return message;
    }


    /**
     * 获取IP
     *
     * @param request
     * @return
     */
    public static String getIpAddressa(HttpServletRequest request) {
        String Xip = request.getHeader("X-Real-IP");
        String XFor = request.getHeader("X-Forwarded-For");

        //多次反向代理后会有多个ip值,第一个ip才是真实ip
        if (StringUtils.isNotEmpty(XFor) && !"unKnown".equalsIgnoreCase(XFor)) {
            int index = XFor.indexOf(",");
            if (index != -1) {
                return "0:0:0:0:0:0:0:1".equals(XFor.substring(0, index)) ? "127.0.0.1" : XFor.substring(0, index);
            } else {
                return "0:0:0:0:0:0:0:1".equals(XFor) ? "127.0.0.1" : XFor;
            }
        }
        XFor = Xip;
        if (StringUtils.isNotEmpty(XFor) && !"unKnown".equalsIgnoreCase(XFor))
            return "0:0:0:0:0:0:0:1".equals(XFor) ? "127.0.0.1" : XFor;

        if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor))
            XFor = request.getHeader("Proxy-Client-IP");

        if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor))
            XFor = request.getHeader("WL-Proxy-Client-IP");

        if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor))
            XFor = request.getHeader("HTTP_CLIENT_IP");

        if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor))
            XFor = request.getHeader("HTTP_X_FORWARDED_FOR");

        if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor))
            XFor = request.getRemoteAddr();

        return "0:0:0:0:0:0:0:1".equals(XFor) ? "127.0.0.1" : XFor;
    }


}

6.在Controller中添加注解

@OperateLog(operateModul = "模块名", operateType = "查询" ,operateDesc = "描述")
@ApiOperation("通过名字获取详细信息")  //swagger注解 
@GetMapping("selectByName/{name}")
public Result selectByName(@PathVariable("name") String name) {
    return baseInfoService.selectByName(name);
}
posted @ 2021-08-16 10:04  RNGException  阅读(704)  评论(0编辑  收藏  举报