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);
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM