五、登錄日志、操作日志

1. 登錄日志

1.1 登錄成功、失敗的日志示例
    // 記錄登錄成功日志
    AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
    // 記錄登錄失敗日志(異常信息為:用戶不存在/密碼錯誤)
    AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
1.2 AsyncFactory.recordLogininfor
    // recordLogininfor用來創(chuàng)建SysLogininfor對象,并插入到數(shù)據(jù)庫中,并將這些操作封裝成實現(xiàn)了Runnable的TimerTask,交由線程池異步調(diào)度、執(zhí)行
    public static TimerTask recordLogininfor(final String username, final String status, final String message,
            final Object... args) {
        // 創(chuàng)建任務(wù)(TimeTask實現(xiàn)了Runnable,可作為一個任務(wù)交由線程池執(zhí)行,由線程池異步調(diào)度執(zhí)行)
        return new TimerTask() {
            // 重寫run方法
            @Override
            public void run() {
                // 構(gòu)造登錄日志對象
                SysLogininfor logininfor = new SysLogininfor();
                // 設(shè)置登錄日志信息
                logininfor.setUserName(username);
                logininfor.setIpaddr(ip);
                logininfor.setLoginLocation(address);
                logininfor.setBrowser(browser);
                logininfor.setOs(os);
                logininfor.setMsg(message);
                // 設(shè)置日志狀態(tài)
                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);
                }
                // 插入日志數(shù)據(jù)
                SpringUtils.getBean(ISysLogininforService.class).insertLogininfor(logininfor);
            }
        };
    }
1.3 AsyncManager.me
public class AsyncManager {
  
    private AsyncManager(){}
    // 餓漢式單例模式
    private static AsyncManager me = new AsyncManager();

    public static AsyncManager me() {
        return me;
    }

    // 其余(略)
}
1.4 AsyncManager.execute
public class AsyncManager {

    // 操作延遲10毫秒
    private final int OPERATE_DELAY_TIME = 10;

    // (通過beanName獲取bean)
    // 異步操作任務(wù)調(diào)度線程池(實現(xiàn)類是ScheduledThreadPoolExecutor,
    // ScheduledThreadPoolExecutor可用來執(zhí)行周期性、延遲性任務(wù))
    private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService");

    // 執(zhí)行任務(wù)
    public void execute(TimerTask task) {
        // 延遲10毫秒執(zhí)行
        executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);
    }
}
1.5 創(chuàng)建ScheduledExecutorService
    @Bean(name = "scheduledExecutorService")
    protected ScheduledExecutorService scheduledExecutorService()
    {
        return new ScheduledThreadPoolExecutor(corePoolSize,
                new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(),
                new ThreadPoolExecutor.CallerRunsPolicy())
        {
            // 這里重寫了afterExecute方法,如果執(zhí)行任務(wù)時出現(xiàn)異常,會將異常傳遞到afterExecute的參數(shù)中
            @Override
            protected void afterExecute(Runnable r, Throwable t)
            {
                super.afterExecute(r, t);
                // 記錄異常信息
                Threads.printException(r, t);
            }
        };
    }

????綜上,可知記錄登錄日志是先通過AsyncFactory.recordLogininfor創(chuàng)建SysLogininfor并插入到數(shù)據(jù)庫中,并將這些操作封裝成實現(xiàn)了Runnable的TimerTask,之后交由ScheduledThreadPoolExecutor異步調(diào)度,延遲10毫秒執(zhí)行。

2. 操作日志

2.1 操作日志示例

????在需要記錄操作日志的方法上貼上@Log注解,并傳入模塊名、功能名等參數(shù)對日志屬性進行設(shè)置:

    @PreAuthorize("@ss.hasPermi('system:config:add')")
    @Log(title = "參數(shù)管理", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(@Validated @RequestBody SysConfig config)
    {
        if (UserConstants.NOT_UNIQUE.equals(configService.checkConfigKeyUnique(config)))
        {
            return AjaxResult.error("新增參數(shù)'" + config.getConfigName() + "'失敗,參數(shù)鍵名已存在");
        }
        config.setCreateBy(getUsername());
        return toAjax(configService.insertConfig(config));
    }
2.2 操作日志切面

????在LogAspect中,會以@Log注解為織入點,在方法返回后或拋異常后對貼有@Log注解的方法進行處理:

// 日志切面
@Aspect
@Component
public class LogAspect {
    private static final Logger log = LoggerFactory.getLogger(LogAspect.class);

    // 方法返回后執(zhí)行
    // jsonResult是方法的返回值
    @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) {
        // 調(diào)用handleLog處理@Log注解及請求、響應(yīng)參數(shù)
        handleLog(joinPoint, controllerLog, null, jsonResult);
    }

    // 拋異常后執(zhí)行
    // e為異常對象
    @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) {
        // 調(diào)用handleLog處理@Log注解及請求、響應(yīng)參數(shù)
        handleLog(joinPoint, controllerLog, e, null);
    }

}
LogAspect.handleLog
    protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) {
        try {
            // 獲取當(dāng)前登錄用戶
            LoginUser loginUser = SecurityUtils.getLoginUser();

            // 創(chuàng)建操作日志對象
            SysOperLog operLog = new SysOperLog();
            // 將操作狀態(tài)設(shè)置為成功
            operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
            // 請求的地址
            String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
            operLog.setOperIp(ip);
            // 設(shè)置請求url
            operLog.setOperUrl(ServletUtils.getRequest().getRequestURI());
            if (loginUser != null) {
                // 設(shè)置操作人
                operLog.setOperName(loginUser.getUsername());
            }
            // e不為null,即方法拋異常了
            if (e != null) {
                // 將操作狀態(tài)設(shè)置為失敗,并設(shè)置異常信息
                operLog.setStatus(BusinessStatus.FAIL.ordinal());
                operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
            }
            // 設(shè)置方法名稱
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            operLog.setMethod(className + "." + methodName + "()");
            // 設(shè)置請求方式
            operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
            // 將controllerLog中配置的參數(shù)信息設(shè)置到operLog中
            // 將joinPoint中的請求信息設(shè)置到operLog中
            // 將jsonResult中的響應(yīng)信息設(shè)置到operLog中
            getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);
            // AsyncFactory.recordOper:將操作日志插入到數(shù)據(jù)庫中并封裝成實現(xiàn)了Runnable的TimerTask作為線程池的任務(wù)
            // AsyncManager.me().execute:將任務(wù)放入ScheduledThreadPoolExecutor中,延遲10毫秒執(zhí)行
            AsyncManager.me().execute(AsyncFactory.recordOper(operLog));
        } catch (Exception exp) {
            // 記錄本地異常日志
            log.error("==前置通知異常==");
            log.error("異常信息:{}", exp.getMessage());
            exp.printStackTrace();
        }
    }

????綜上,可知記錄登錄日志、操作日志是先創(chuàng)建對應(yīng)的日志對象并插入到數(shù)據(jù)庫中,并將這些操作封裝成實現(xiàn)了Runnable的TimerTask,之后交由ScheduledThreadPoolExecutor異步調(diào)度,延遲10毫秒執(zhí)行。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容