SpringBoot+SpringAOP整合系统日志统计

16 篇文章 0 订阅
12 篇文章 0 订阅

最近做一个关于系统日志统计相关的功能,主要统计的是controller层的操作,比较简单,没有涉及统计service层和dao层的。

系统架构:SpringBoot、Shiro、Mybatis、thymelaf

问题分析:系统日志统计 其实已经很成熟了,网上随便一搜都能找到较为完善的解决思路及代码示例,我再写一遍,仅仅是为了对新学习的技术起到一个巩固、复盘的一个过程。

首先:统计系统访问日志,必然少不了日志表,还应该设计其主要统计的字段有哪些:

/** 主键ID */
private Integer logId;
/** 日志描述 */
private String description;
/** 日志类型常量 */
private String operationType;
/** 请求类型 */
private String httpMethod;
/** ip */
private String ip;
/** 记录的类方法 */
private String classMethod;
/** 参数列表 */
private String args;
/** 主要参数 */
private Integer primaryId;
/** 创建时间 */
private Date addTime;
/**
 * 表名后缀
 */
private String tableSuffix;

之所以定义表名后缀,是因为系统中要统计各个模块下的操作记录,为了便于区分,那么每个模块的操作记录存放为一张表,都是以主表来衍生子表。

采用SpringAOP的方式,首先需要建立枚举类,用于自定义注解的

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface SystemLog {
    /**
 * 日志表后缀,可以根据业务表自定义业务日志存储在哪张表中,前缀固定为z_system_log_
 * 默认的Constant.BASE是存储在 z_system_log表中
 * @return
 */
Constant suffix() default Constant.BASE;

/**
 * 日志描述/说明
 * @return
 */
String desc() default  "";

/**
 * 当前记录信息的主键ID名称,如:z_system_log映射的实体类主键名称为:logId,则填logId
 * @return
 */
String primaryName() default "";

/**
 * 操作类型,数据记录主表时需要传入
 * @return
 */
SystemLogType operationType() d

然后定义系统日志的切面,将需要统计的controller定义切点并织入切点

@Aspect
@Component
@Slf4j
public class SystemLogAspect {

    @Autowired
    private IZSystemLogService systemLogService;
    /**
     * 定义切点
     */
    @Pointcut("execution(public * com.ruoyi.project.system.*.controller..*.*(..))")
    public void systemLog() {}

    /**
     * 前置通知:在连接点之前执行的通知
     * @param joinPoint
     * @throws Throwable
     */
    @Before("systemLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        // 织入切点
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 获取切入点所在的方法
        Method method = signature.getMethod();

        // 获取操作
        SystemLog systemLog1 = method.getAnnotation(SystemLog.class);
        //不需要记录日志
        if(systemLog1 == null) {
            return;
        }
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        ZSystemLog systemLog = ZSystemLog.builder()
                //日志描述
                .description("'" + systemLog1.desc() + "'")
                //日志类型常量
                .operationType("'" + systemLog1.operationType().toString() + "'")
                //请求类型
                .httpMethod("'" + request.getMethod() + "'")
                //ip
                .ip("'" + request.getRemoteAddr() + "'")
                //记录的类方法
                .classMethod("'" + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName() + "'")
                //参数列表
                .args("'" + Arrays.toString(joinPoint.getArgs()) + "'")
                //主要参数
                .primaryId(getPrimaryId(systemLog1.primaryName(), request, signature, joinPoint.getArgs()))
                .build();
        systemLogService.saveSystemLog(systemLog, systemLog1.suffix().toString());
    }
    @AfterReturning(returning = "ret", pointcut = "systemLog()")
    public void doAfterReturning(Object ret) throws Throwable {

    }

    /**
     * 获取需要存储的主键信息
     * @param primaryName
     * @param request
     * @return
     */
    private int getPrimaryId(String primaryName, HttpServletRequest request, MethodSignature signature, Object[] args) {
        int primaryId = -1;
        if(StringUtils.isEmpty(primaryName)) {
            return primaryId;
        }
        String pVal = request.getParameter(primaryName);
        if(!StringUtils.isEmpty(pVal)) {
            return Integer.valueOf(pVal);
        }
        Object aVal = request.getAttribute(primaryName);
        if(!StringUtils.isEmpty(aVal)) {
            return Integer.valueOf(aVal.toString());
        }
        String[] parameterNames = signature.getParameterNames();
        for (int i = 0; i < parameterNames.length; i++) {
            if(parameterNames[i].toLowerCase().equals(primaryName.toLowerCase())) {
                primaryId = Integer.valueOf(args[i].toString());
                break;
            }
        }
        return primaryId;
    }

}

然后定义要统计模块的系统常量,

/**
 * @Auther: zh
 * @Date: 2020-07-04
 * @Description: 日志类型常量,主要记录模块下的日志
 */
public enum Constant {
    // 日志用到的常量定义,用于判断日志类型
    /**
     * 基础日志类型,统一记录到z_system_log
     */
    BASE,

    /**
     * 日志主表名称
     */
    Z_SYSTEM_LOG,

    /**
     * 【XXX模块】
     */
    XVUNIT_INFO,
}

=============
public enum SystemLogType {

    /**
     * 基础操作
     */
    OPERATION,

    /**
     * 【XXX模块】 
     */
    XVUNIT_VISIT,

}

统计系统日志,无非就是两种情况:

  1. 有表,则直接将参数插入基础系统日志表中
  2. 无表,则先创建系统日志表,以基础表作为前缀,以模块自定义常量最为表后缀,创建表,然后再插入数据。
  • 刚才创建的切面中有一个构建方法,该方法在所有Controller中使用了自定义注解@SystemLog的方法执行前都会进入该切面,执行方法

@Override
public void saveSystemLog(ZSystemLog systemLog, String tableSuffix) {
    //1、判断表后缀是否和基础表相同
    tableSuffix = tableSuffix.toUpperCase().equals(Constant.BASE.toString()) ? null : tableSuffix;
    //2、如果为空,则以基础表为表名,否则以基础表加上表后缀组成新表
    String tableName = StringUtils.isEmpty(tableSuffix) ? Constant.Z_SYSTEM_LOG.toString() : Constant.Z_SYSTEM_LOG.toString() + "_" + tableSuffix;
   //3、判断表名是否存在,不存在创建表
     if(!existTable(tableName)) {
        createNewSystemLogTable(tableName);
    }
    zSystemLogMapper.saveSystemLog(systemLog, tableName);
}

@Override
public boolean existTable(String tableName) {
    int tableNum = zSystemLogMapper.existTable(tableName);
    return tableNum > 0 ? true : false;
}

@Override
public void createNewSystemLogTable(String tableName) {
    zSystemLogMapper.createNewSystemLogTable(tableName);
}
  • 建表语句以及查询表名语句
<select id="existTable" parameterType="String" resultType="Integer">
    select COUNT(1)  from INFORMATION_SCHEMA.TABLES
    where TABLE_SCHEMA = '你的数据库名' and TABLE_NAME = #{tableName};

</select>

<update id="createNewSystemLogTable" parameterType="String">
    CREATE TABLE  ${tableName} (
      `log_id` int(10) NOT NULL AUTO_INCREMENT,
      `description` varchar(64) DEFAULT NULL,
      `operation_type` VARCHAR(64) DEFAULT null,
      `http_method` varchar(8) DEFAULT NULL,
      `ip` varchar(32) DEFAULT NULL,
      `class_method` varchar(255) DEFAULT NULL,
      `args` varchar(128) DEFAULT NULL,
      `primary_id` int(10) DEFAULT NULL,
      `add_time` datetime DEFAULT NULL,
      PRIMARY KEY (`log_id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
</update>

<insert id="saveSystemLog" statementType="STATEMENT">
    INSERT INTO ${tableName} (
      description,
      operation_type,
      http_method,
      ip,
      class_method,
      args,
      primary_id,
      add_time
    )
    VALUES (
      ${systemLog.description},
      ${systemLog.operationType},
      ${systemLog.httpMethod},
      ${systemLog.ip},
      ${systemLog.classMethod},
      ${systemLog.args},
      ${systemLog.primaryId},
      NOW()
    )
</insert>

 

  • 在需要统计的方法上加入自定义注解@SystemLog即可

**
*suffix表后缀
*desc:日志描述
*operationType:操作类型
/
@GetMapping()
@SystemLog(suffix = Constant.XVUNIT_INFO,desc = "XXX模块日志统计",operationType = SystemLogType.XVUNIT_VISIT)
public String list() {
    return prefix + "/list";
}

以上就是采用SpringAOP实现系统日志统计方法。

如果要讲各个模块统计的日志,数据可视化的呈现出来,可以参考echarts,可以数据实时渲染到前端页面,且提供了很多可用的主题。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值