Springboot AOP 自定義註解實現系統日誌

不夜De星空發表於2019-08-08

一、新增AOP依賴

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

二、AOP切點類

@Slf4j
@Component
@Aspect
@AllArgsConstructor
public class SysLogAspect implements Ordered {

    private final SysLogRepository sysLogRepository;

    private final SmUserQuery smUserQuery;

    private static final ThreadLocal<SysLog> SYS_LOG_THREAD_LOCAL = ThreadLocal.withInitial(SysLog::new);

    /**
     * 通過AOP方式,攔截註解的日誌
     **/
    @Pointcut(value = "@annotation(com.huiju.piccre.lfxy.system.annotation.SysLogging)")
    public void logAspect() {
    }

    /**
     * 環繞增強,相當於MethodInterceptor
     **/
    @Around("logAspect()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Object res = null;
        long time = System.currentTimeMillis();
        SysLog sysLog = SYS_LOG_THREAD_LOCAL.get();
        try {
            res = joinPoint.proceed();
            time = System.currentTimeMillis() - time;
            return res;
        } catch (Exception ex) {
            sysLog.setOperationStatus("FAILED");
            sysLog.setReturnMsg("操作失敗!異常資訊:——> " + ex.getMessage());
            throw ex;
        } finally {
            try {
                MethodSignature signature = (MethodSignature) joinPoint.getSignature();
                ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                HttpServletRequest request = attributes.getRequest();
                sysLog.setOperator(smUserQuery.currentUserCode());
                sysLog.setMethodName(signature.getDeclaringTypeName() + "." + signature.getName());
                sysLog.setMethodArgs(JSON.toJSONString(joinPoint.getArgs()));
                sysLog.setReturnValue(JSON.toJSONString(res));
                sysLog.setRunningTime(time);
                sysLog.setRequestUrl(request.getRequestURL().toString());
                sysLog.setIpAddr(request.getRemoteAddr());
                sysLog.setOperationTime(DateUtil.getNowDate());
                SysLogging annotation = signature.getMethod().getAnnotation(SysLogging.class);
                if (annotation != null) {
                    sysLog.setOperationType(annotation.operationType().getValue());
                    sysLog.setOperateContent(annotation.operateContent());
                    sysLog.setModuleName(annotation.moduleName());
                }
            } catch (Exception ex) {
                log.error("LogAspect 操作失敗:" + ex.getMessage());
                ex.printStackTrace();
            }
        }
    }

    /**
     * 持久化日誌資料
     **/
    private void saveSysLog(SysLog sysLog) {
        log.info("記錄日誌:" + sysLog.toString());
        //持久化日誌資料
        sysLogRepository.saveAndFlush(sysLog);
    }

    @Before("logAspect()")
    public void doBeforeAdvice(JoinPoint joinPoint) {
        log.info("進入方法前執行.....");
        SysLog sysLog = SYS_LOG_THREAD_LOCAL.get();
        sysLog.setGid(IdUtils.uuid32());
        sysLog.setOperationStatus("SUCCESS");
        sysLog.setReturnMsg("操作成功!");
    }

    /**
     * 處理完請求,返回內容
     *
     * @param ret
     */
    @AfterReturning(returning = "ret", pointcut = "logAspect()")
    public void doAfterReturning(Object ret) {
        log.info("方法的返回值 : " + ret);
    }

    /**
     * 後置異常通知
     */
    @AfterThrowing("logAspect()")
    public void doAfterThrowing(JoinPoint joinPoint) {
        log.info("方法異常時執行.....");
    }

    /**
     * 後置最終通知,final增強,不管是丟擲異常或者正常退出都會執行
     */
    @After("logAspect()")
    public void doAfterAll(JoinPoint joinPoint) {
        log.info("方法最後執行.....");
        //方法執行完成後增加日誌
        saveSysLog(SYS_LOG_THREAD_LOCAL.get());
    }

    @Override
    public int getOrder() {
        return 1;
    }
}

三、自定義註解

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

    /**
     * 操作型別
     **/
    OperationTypeEnum operationType() default OperationTypeEnum.UNDEFINE;

    /**
     * 執行模組
     **/
    String moduleName() default "";

    /**
     * 操作內容描述
     **/
    String operateContent() default "";

}

四、實體類

@Builder
@Data
@Entity
@NoArgsConstructor
@AllArgsConstructor
public class SysLog implements Serializable {
    @Id
    @ApiModelProperty(value = "操作日誌id")
    private String gid;
    @ApiModelProperty(value = "操作人")
    private String operator;
    @ApiModelProperty(value = "操作型別")
    private String operationType;
    @ApiModelProperty(value = "執行模組")
    private String moduleName;
    @ApiModelProperty(value = "執行方法")
    private String methodName;
    @ApiModelProperty(value = "執行引數")
    private String methodArgs;
    @ApiModelProperty(value = "返回值")
    private String returnValue;
    @ApiModelProperty(value = "返回訊息")
    private String returnMsg;
    @ApiModelProperty(value = "執行時長")
    private long runningTime;
    @ApiModelProperty(value = "操作內容")
    private String operateContent;
    @ApiModelProperty(value = "請求路徑")
    private String requestUrl;
    @ApiModelProperty(value = "IP地址")
    private String ipAddr;
    @ApiModelProperty(value = "操作時間")
    private Date operationTime;
    @ApiModelProperty(value = "執行描述(1:執行成功、2:執行失敗)")
    private String operationStatus;
}

五、操作型別列舉

public enum OperationTypeEnum {
    /**
     * 操作型別
     */
    UNDEFINE("undefine"),
    DELETE("delete"),
    SELECT("select"),
    UPDATE("update"),
    INSERT("insert");

    private String value;

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    OperationTypeEnum(String s) {
        this.value = s;
    }
}

六、日誌表設計

CREATE TABLE `sys_log` (
  `gid` varchar(32) NOT NULL COMMENT '操作日誌id',
  `operator` varchar(32) DEFAULT NULL COMMENT '操作人',
  `operation_type` varchar(10) DEFAULT '' COMMENT '操作型別',
  `running_time` bigint(20) DEFAULT NULL COMMENT '方法執行時間',
  `module_name` varchar(32) DEFAULT NULL COMMENT '執行模組',
  `method_name` varchar(200) DEFAULT NULL COMMENT '執行方法',
  `method_args` text COMMENT '執行引數',
  `return_value` text COMMENT '返回值',
  `return_msg` varchar(640) DEFAULT NULL COMMENT '返回訊息',
  `operate_content` varchar(480) DEFAULT NULL COMMENT '操作內容',
  `request_url` varchar(360) DEFAULT NULL COMMENT '請求路徑',
  `ip_addr` varchar(200) DEFAULT NULL COMMENT 'IP地址',
  `operation_time` datetime DEFAULT NULL COMMENT '操作時間',
  `operation_status` char(10) DEFAULT NULL COMMENT '執行描述(1:執行成功、2:執行失敗)',
  PRIMARY KEY (`gid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

 

相關文章