RuoYi 异步日志

561 阅读2分钟

在若依中一些日志的记录是异步处理的,与业务逻辑进行解耦,提高响应效率

  catch (Exception e)
         {
             if (e instanceof BadCredentialsException)
             {
                 AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
                 throw new UserPasswordNotMatchException();
             }
             else
             {
                 AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
                 throw new ServiceException(e.getMessage());
             }
         }
         finally
         {
             AuthenticationContextHolder.clearContext();
         }
         AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
        

我们可以看到若依封装了一个异步任务管理器,是以饿汉式的单例模式为全局唯一对象。

调用execute方法延迟10s执行任务,需要传入一个Runnable

 public ScheduledFuture<?> schedule(Runnable command,
                                        long delay, TimeUnit unit);

这里传入的是TimerTask,其实现了Runnable接口

 public abstract class TimerTask implements Runnable

A task that can be scheduled for one-time or repeated execution by a Timer.

Timer是一种定时器工具,用来在一个后台线程计划执行指定任务。它可以计划执行一个任务一次或反复多次。 TimerTask一个抽象类,它的子类代表一个可以被Timer计划的任务。

若依使用异步工厂创建TimerTask

 ​
 /**
  * 异步工厂(产生任务用)
  * 
  * @author ruoyi
  */
 public class AsyncFactory
 {
     private static final Logger sys_user_logger = LoggerFactory.getLogger("sys-user");
 ​
     /**
      * 记录登录信息
      * 
      * @param username 用户名
      * @param status 状态
      * @param message 消息
      * @param args 列表
      * @return 任务task
      */
     public static TimerTask recordLogininfor(final String username, final String status, final String message,
             final Object... args)
     {
         final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
         final String ip = IpUtils.getIpAddr();
         return new TimerTask()
         {
             @Override
             public void run()
             {
                 String address = AddressUtils.getRealAddressByIP(ip);
                 StringBuilder s = new StringBuilder();
                 s.append(LogUtils.getBlock(ip));
                 s.append(address);
                 s.append(LogUtils.getBlock(username));
                 s.append(LogUtils.getBlock(status));
                 s.append(LogUtils.getBlock(message));
                 // 打印信息到日志
                 sys_user_logger.info(s.toString(), args);
                 // 获取客户端操作系统
                 String os = userAgent.getOperatingSystem().getName();
                 // 获取客户端浏览器
                 String browser = userAgent.getBrowser().getName();
                 // 封装对象
                 SysLogininfor logininfor = new SysLogininfor();
                 logininfor.setUserName(username);
                 logininfor.setIpaddr(ip);
                 logininfor.setLoginLocation(address);
                 logininfor.setBrowser(browser);
                 logininfor.setOs(os);
                 logininfor.setMsg(message);
                 // 日志状态
                 if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER))
                 {
                     logininfor.setStatus(Constants.SUCCESS);
                 }
                 else if (Constants.LOGIN_FAIL.equals(status))
                 {
                     logininfor.setStatus(Constants.FAIL);
                 }
                 // 插入数据
                 SpringUtils.getBean(ISysLogininforService.class).insertLogininfor(logininfor);
             }
         };
     }
 ​
     /**
      * 操作日志记录
      * 
      * @param operLog 操作日志信息
      * @return 任务task
      */
     public static TimerTask recordOper(final SysOperLog operLog)
     {
         return new TimerTask()
         {
             @Override
             public void run()
             {
                 // 远程查询操作地点
                 operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp()));
                 SpringUtils.getBean(ISysOperLogService.class).insertOperlog(operLog);
             }
         };
     }
 }

若依在aop中记录异常日志也是通过异步任务管理器去创建的

 /**
      * 拦截异常操作
      * 
      * @param joinPoint 切点
      * @param e 异常
      */
     @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")
     public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e)
     {
         handleLog(joinPoint, controllerLog, e, null);
     }
 ​
     protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult)
     {
         try
         {
             // 获取当前的用户
             LoginUser loginUser = SecurityUtils.getLoginUser();
 ​
             // *========数据库日志=========*//
             SysOperLog operLog = new SysOperLog();
             operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
             // 请求的地址
             String ip = IpUtils.getIpAddr();
             operLog.setOperIp(ip);
             operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255));
             if (loginUser != null)
             {
                 operLog.setOperName(loginUser.getUsername());
             }
 ​
             if (e != null)
             {
                 operLog.setStatus(BusinessStatus.FAIL.ordinal());
                 operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
             }
             // 设置方法名称
             String className = joinPoint.getTarget().getClass().getName();
             String methodName = joinPoint.getSignature().getName();
             operLog.setMethod(className + "." + methodName + "()");
             // 设置请求方式
             operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
             // 处理设置注解上的参数
             getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);
             // 设置消耗时间
             operLog.setCostTime(System.currentTimeMillis() - TIME_THREADLOCAL.get());
             // 保存数据库
             AsyncManager.me().execute(AsyncFactory.recordOper(operLog));
         }
         catch (Exception exp)
         {
             // 记录本地异常日志
             log.error("异常信息:{}", exp.getMessage());
             exp.printStackTrace();
         }
         finally
         {
             TIME_THREADLOCAL.remove();
         }
     }

最后在来看一看使用的定时任务线程池

 /**
      * 执行周期性或定时任务
      */
     @Bean(name = "scheduledExecutorService")
     protected ScheduledExecutorService scheduledExecutorService()
     {
         return new ScheduledThreadPoolExecutor(corePoolSize,
                 new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(),//线程创建工厂
                 new ThreadPoolExecutor.CallerRunsPolicy())//当前调用者的线程中执行任务
         {
             @Override
             protected void afterExecute(Runnable r, Throwable t)
             {
                 super.afterExecute(r, t);
                 Threads.printException(r, t);
             }
         };
     }