spring boot学习系列之统一日志处理7

在安全审计方面或者日志分析中,很多时候都要记录用户的操作行为,特别是用户登录和特殊模块的操作。下面学习日志统一处理。将使用AOP、采用注解切点的方式做个横向的操作日志写入数据库操作。

定义AOP日志处理类,将自定义注解作为切点,目标方法执行后,构造日志,写入数据库。

  1. @Aspect
  2. @Component
  3. public class LogAopComponent {
  4.     private static final Logger logger = LoggerFactory.getLogger(LogAopComponent.class);
  5.     @Autowired
  6.     ExceptionLogExecutors executors;
  7.     /**
  8.      * 注解切点
  9.      * 
  10.      * @author wenqy
  11.      */
  12.     @Pointcut(“@annotation(com.wenqy.log.OperateLog)”)
  13.     private void pointCutMethod(){}
  14.     /**
  15.      * 基于切点
  16.      * @param joinPoint
  17.      * @author wenqy
  18.      * @throws ClassNotFoundException 
  19.      */
  20.     @After(“pointCutMethod()”)
  21.     public void recordOperateLog(JoinPoint joinPoint) throws ClassNotFoundException{
  22.         OperateLogVO operateLog = createOperateLog(joinPoint);
  23.         // 扔到线程池执行
  24.         this.executors.execute(new OperateLogThread(operateLog ));
  25.     }
  26.     /**
  27.      * 获取操作日志
  28.      * @param joinPoint
  29.      * @return
  30.      * @throws ClassNotFoundException
  31.      * @author wenqy
  32.      */
  33.     public OperateLogVO createOperateLog(JoinPoint joinPoint) throws ClassNotFoundException {
  34.         OperateLogVO operateLog = new OperateLogVO();
  35.         String clientIp = WebUtils.getClientIp();
  36.         operateLog.setHostIp(clientIp);
  37.         // TODO 取session
  38.         operateLog.setUserId(IdGenerator.randomLong());
  39.         operateLog.setOperateTime(new Date());
  40.         // 获取操作方法名
  41.         String methodName = joinPoint.getSignature().getName();
  42.         // 获取方法类名
  43.         String targetName = joinPoint.getTarget().getClass().getName();
  44.         Class<?> targetClass = Class.forName(targetName);
  45.         Method[] methods = targetClass.getMethods();
  46.         for (Method method : methods){
  47.             if(!method.getName().equals(methodName)){
  48.                 continue;
  49.             }
  50.             // 获取方法注解
  51.             OperateLog logAnnotation = method.getAnnotation(OperateLog.class);
  52.             operateLog.setAction(logAnnotation.action());
  53.             operateLog.setModule(logAnnotation.module());
  54.             operateLog.setRemark(logAnnotation.remark());
  55.             operateLog.setSubSystem(logAnnotation.subSystem());
  56.             break;
  57.         }
  58.         return operateLog;
  59.     }
  60. }

自定义切点注解,描述日志信息。

  1. /**
  2.  * 日志注解
  3.  * 
  4.  * @version V5.0
  5.  * @author wenqy
  6.  * @date   2018年2月14日
  7.  */
  8. @Retention(RetentionPolicy.RUNTIME)
  9. @Target(ElementType.METHOD) // 目标方法
  10. @Documented
  11. public @interface OperateLog {
  12.     /**
  13.      * 子系统
  14.      * @return
  15.      * @author wenqy
  16.      */
  17.     String subSystem() default “”;
  18.     /**
  19.      * 模块
  20.      * @return
  21.      * @author wenqy
  22.      */
  23.     String module() default “”;
  24.     /**
  25.      * 动作
  26.      * @return
  27.      * @author wenqy
  28.      */
  29.     String action() default “”;
  30.     /**
  31.      * 备注
  32.      * @return
  33.      * @author wenqy
  34.      */
  35.     String remark() default “”;
  36. }

根据注解信息和用户信息,定义操作日志VO类

  1. public class OperateLogVO  implements Serializable {
  2.     private Long id;
  3.     private String subSystem;
  4.     private String module;
  5.     private String action;
  6.     private String remark;
  7.     private String hostIp; // ip
  8.     private Date operateTime; // 操作时间
  9.     private Long userId; // 操作用户Id
  10. // 忽略setter getter
  11. }

定义保存操作日志线程

  1. /**
  2.  * 操作日志保存线程
  3.  * 
  4.  * @version V5.0
  5.  * @author wenqy
  6.  * @date   2018年2月14日
  7.  */
  8. public class OperateLogThread implements Runnable {
  9.     private OperateLogVO operateLogVO;
  10.     public OperateLogThread(OperateLogVO operateLogVO) {
  11.         this.operateLogVO = operateLogVO;
  12.     }
  13.     @Override
  14.     public void run() {
  15.         // 加载异常日志处理服务
  16.         OperateLogService logService = (OperateLogService) SpringAppContextHolder
  17.                 .getBean(OperateLogService.class);
  18.         logService.saveOperateLog(operateLogVO);
  19.     }
  20. }

控制层中要记录操作日志的方法定义引用

  1. @OperateLog(subSystem=“系统管理”, module=“系统登录”, action=“login”,remark=“用户登录”)
  2. @RequestMapping(value = “/doLogin”)
  3. @ResponseBody
  4. public Object doLogin(HttpServletRequest request) {
  5.        String email = request.getParameter(“email”);
  6.        String password = request.getParameter(“password”);
  7.        System.out.println(email + ” “ + password);
  8.        return “success”;
  9. }

启动应用,访问/doLogin URL链接,查看是否是否插库

operator_log

使用的线程池为上篇spring boot学习系列之统一异常处理6中定义的线程池,保存操作日志的Service和Mybatis Mapper也与上篇异常日志保存类似,代码就不贴了。也有采用父类定义操作日志方法或者利用拦截器的写法,这些都可以实现。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

3 + 9 = ?