做系统的关键操作的日志功能

问题:

项目系统需要记录用户的关键操作日志,以便后期的系统维护,方便的查看问题,及时排除等原因。

分析:

  • 作为一个日志记录功能,首先数据库新建一张表保存用户的操作关键字段,用户名,ip,操作描述,时间,日志id

  • 采用技术:

    • 第一种:新建一个日志业务实现,在操作发生时进行联动同,缺点是耦合太紧密,无用代码增多,后期代码臃肿,改动时地方分散,不利于维护

    • 第二种:使用spring 的 aop 技术进行切面切入由于本身系统结构不规范,参数,方法名没有一致的样式,使用正则匹配方法名不是很方便,本身日志记录也只是记录关键操作,api全部切入的话,就不需要这个功能了,必须有针对性的记录关键操作日志

    • 第三种:使用spring 的 aop技术切到自定义注解上,针对不同注解标志进行参数解析,记录日志,缺点是要针对每个不同的注解标志进行分别取注解标志,获取参数进行日志记录输出

采用第三种方法

思路

1. 通过自定义注解,注解到需要aop切入的方法上
2. 声明一个aspect切入面,注入数据层dao, 将上面的注解类设成切入点, 通过反射获取到自定义注解上的某个属性,来区分是不同的记录日志需求。进行不同程度的封装Log实体对象。
3. 然后通过数据层,写入到日志表。

步骤

  1. 自定义方法注解类,注解到方法上,用于标识需要切入的点
/**
 * 日志切面注解
 */
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MethodLog {

    /**
     * 该注解作用于方法上时需要备注信息
     */
    String remark() default "";
    String operType() default "0";
}

  1. 切面的实现
/**
 * 日志切面实现
 */

@Component
@Aspect
public class LogService {

    @Autowired
    private OptLogDao dao;

    public LogService() {
        System.out.println("Aop");
    }

    /**
     * 切点
     */
    @Pointcut("@annotation(com.education.anno.MethodLog)")
    public void methodCachePointcut() {
    }


    /**
     * 切面
     *
     * @param point
     * @return
     * @throws Throwable
     */
    @Around("methodCachePointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {

        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
                .getRequestAttributes()).getRequest();
        String ip = getIp(request);
        User user = UserUtil.getCurrentUser();
        String methodRemark = getMthodRemark(point);
        Object[] method_param;

        Object object;
        try {
            method_param = point.getArgs(); //获取方法参数
            // String param=(String) point.proceed(point.getArgs());
            object = point.proceed();
        } catch (Exception e) {
            // 异常处理记录日志..log.error(e);
            throw e;
        }
        LiveOptLog optLog = new LiveOptLog();
        optLog.setUserId(user.getId().intValue());
        optLog.setIp(ip);
        optLog.setUserName(user.getUsername());

        /**
         * 课程操作
         */
        if ("新增课程".equals(methodRemark) || "修改课程".equals(methodRemark) || "删除课程".equals(methodRemark)) {
            if (method_param[0] instanceof LiveCourseInfo) {
                LiveCourseInfo courseInfo = (LiveCourseInfo) method_param[0];
                optLog.setDescription("用户 " + user.getUsername() + " " + methodRemark + "id: " + courseInfo.getId());
            }
        } 

        /**
         * 课节操作
         */
        if ("新增课节".equals(methodRemark) || "修改课节".equals(methodRemark) || "删除课节".equals(methodRemark)) {
            if (method_param[0] instanceof LiveCourseLessonInfo) {
                LiveCourseLessonInfo lessonInfo = (LiveCourseLessonInfo) method_param[0];
                optLog.setDescription("用户 " + user.getUsername() + " " + methodRemark + "id: " + lessonInfo.getId());
            }
        }

        dao.insertSelective(optLog);
        return object;

    }

    /**
     * 获取请求ip
     *
     * @param request
     * @return
     */
    public static String getIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
            int index = ip.indexOf(",");
            if (index != -1) {
                return ip.equals("0:0:0:0:0:0:0:1")?"127.0.0.1":ip.substring(0, index);
            } else {
                return ip.equals("0:0:0:0:0:0:0:1")?"127.0.0.1":ip;
            }
        }
        ip = request.getHeader("X-Real-IP");
        if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
            return ip;
        }
        return request.getRemoteAddr().equals("0:0:0:0:0:0:0:1")?"127.0.0.1":ip;
    }

    /**
     * 获取方法中的中文备注
     *
     * @param joinPoint
     * @return
     * @throws Exception
     */
    public static String getMthodRemark(ProceedingJoinPoint joinPoint) throws Exception {

        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();

        Class targetClass = Class.forName(targetName);
        Method[] method = targetClass.getMethods();
        String methode = "";
        for (Method m : method) {
            if (m.getName().equals(methodName)) {
                Class[] tmpCs = m.getParameterTypes();
                if (tmpCs.length == arguments.length) {
                    MethodLog methodCache = m.getAnnotation(MethodLog.class);
                    if (methodCache != null) {
                        methode = methodCache.remark();
                    }
                    break;
                }
            }
        }
        return methode;
    }
  1. 日志实体, 根据需求自定义
@Table(name = "opt_log")
@Data
@NoArgsConstructor
@AllArgsConstructor
@ExcelTitles({"序号", "ID" ,"操作人","描述", "时间", "IP"})
public class LiveOptLog extends BaseModel {

    /**
     * 日志类型
     */
    private String type;

    /**
     * 用户id
     */
    @Column(name = "user_id")
    private Integer userId;

    @Column(name = "user_name")
    private String userName;

    /**
     * 内容
     */
    private String description;

    private String ip;

    private Date createtime;

}
  1. 日志切入, 添加注解
@PostMapping
@ApiOperation(value = "保存")
@MethodLog(remark = "新增课程")
public CourseInfo save(@RequestBody CourseInfo courseInfo) {

    courseInfoServiceImpl.save(courseInfo);

    return courseInfo;
}
  1. 加入springboot配置

<!--spring切面aop依赖-->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

在application.properties文件里加这样一条配置
spring.aop.auto=true

在springboot项目里加这两条配置即可,就可以开启aop功能

ok, 这样每次执行想要拦截关键日志的操作都会被切面拦截

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值