Fork me on GitHub

使用自定义注解实现系统日志记录

本文的方案背景:系统记录所有的敏感操作日志(增删改)

方案技术:无线程池、无监听订阅、无多数据源、无错误消息监控推送。

自定义注解

@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ULog {

    //功能描述
    String option();

    //是否缓存请求参数
    boolean isSaveRequestData() default true;

    //是否缓存响应结果
    boolean isSaveResponseData() default false;
}

Aspect

@Slf4j
@Aspect
@Component
public class ULogAspect {

    @Resource
    SystemLogService systemLogService;

    @Resource
    UserInfoCacheHandler cacheHandler;

    @Pointcut("@annotation(com.hd.uws.common.annotation.ULog)")
    public void logPointCut() {
        //do nothing....
    }

    @Pointcut("@annotation(com.hd.uws.common.annotation.ULog)")
    public void exceptionPointCut() {
        //do nothing....
    }

    /**
     * 方法执行的前后切面处理
     *
     * @param point 切面
     * @return java.lang.Object
     * @author Jackpot
     * @date 2022/5/26 09:12
     */
    @Around("logPointCut()")
    public Object aroundMethod(ProceedingJoinPoint point) throws Throwable {
        //请求开始时间
        long beginTime = System.currentTimeMillis();
        //处理方法执行日志切面(不要捕获异常抛出 由下面的异常方法处理)
        Object result = point.proceed();
        //执行时长(毫秒)
        long endTime = System.currentTimeMillis();
        SavaLogBO logBO = new SavaLogBO();
        logBO.setBeginTime(LocalDateTime.now());
        logBO.setEndTime(LocalDateTime.now());
        logBO.setRuntime(endTime - beginTime);
        logBO.setJoinPoint(point);
        logBO.setResult(result);
        saveLog(logBO);
        //切面返回响应
        return result;
    }

    /**
     * 异常返回通知,用于拦截异常日志信息 连接点抛出异常后执行
     *
     * @param point 切面
     * @param e     异常
     * @author Jackpot
     * @date 2022/5/26 09:12
     */
    @AfterThrowing(pointcut = "exceptionPointCut()", throwing = "e")
    public void afterThrowing(JoinPoint point, Throwable e) {
        //请求开始时间
        long beginTime = System.currentTimeMillis();
        //获取当前请求的错误日志信息
        String exceptionMsg = stackTraceToString(e.getClass().getName(), e.getMessage(), e.getStackTrace());
        long endTime = System.currentTimeMillis();
        long runtime = endTime - beginTime;
        //保存日志
        SavaLogBO logBO = new SavaLogBO();
        logBO.setBeginTime(LocalDateTime.now());
        logBO.setEndTime(LocalDateTime.now());
        logBO.setRuntime(runtime);
        logBO.setJoinPoint(point);
        logBO.setExceptionMsg(exceptionMsg);
        saveLog(logBO);
    }

    /**
     * 记录当前错误请求日志信息
     *
     * @param exceptionName    异常名称
     * @param exceptionMessage 异常内容主体
     * @param elements         元素
     * @return java.lang.String
     * @author Jackpot
     * @date 2022/5/26 09:13
     */
    public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) {
        //定义一个缓存
        StringBuilder strbuff = new StringBuilder();
        //拼接错误信息
        for (StackTraceElement stet : elements) {
            //处理
            strbuff.append(stet).append("\n");
        }
        //返回
        return exceptionName + ":" + exceptionMessage + "\n\t" + strbuff;
    }

    /**
     * 日志入库
     *
     * @param logBO 入库bo
     * @author Jackpot
     * @date 2022/5/26 10:32
     */
    private void saveLog(SavaLogBO logBO) {
        //获取当前注解对象
        ULog uLogAnnotation = getAnnotation(logBO.getJoinPoint());
        //获取自定义的注解描述
        if (uLogAnnotation != null) {
            //定义一个日志对象缓存
            SystemLog sysLog = new SystemLog();
            //获取当前的请求对象
            HttpServletRequest currentRequest = getCurrentRequest(sysLog);
            //获取自定义的操作
            String option = uLogAnnotation.option();
            //注解上的描述
            sysLog.setOption(Strings.isNullOrEmpty(option) ? "操作" : option);
            sysLog.setBeginTime(logBO.getBeginTime());
            sysLog.setEndTime(logBO.getEndTime());
            //缓存当前请求的耗时
            sysLog.setRuntime(logBO.getRuntime());
            //获取请求的url地址
            sysLog.setUrl(currentRequest.getRequestURI());
            //返回发出请求的IP地址
            sysLog.setIp(IpUtils.getIpAddress(currentRequest));
            //请求参数处理
            formatParamEvent(logBO.getJoinPoint(), uLogAnnotation, sysLog);
            //判断当前是否有指定错误信息
            if (Strings.isNullOrEmpty(logBO.getExceptionMsg())) {
                //当前没有指定的错误 表示请求正常
                formatResultEvent(uLogAnnotation, logBO.getResult(), sysLog);
            } else {
                //当前没有指定的错误 表示请求系统异常
                sysLog.setCode(ResCode.SYSTEM_UNKNOWN_ERROR.getCode());
                //系统异常错误信息
                sysLog.setException(logBO.getExceptionMsg());
            }
            //异常捕获处理
            try {
                // 保存系统日志
                systemLogService.add(sysLog);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 获取对应的注解对象
     *
     * @param joinPoint 切点
     * @return com.hd.uws.annotation.ULog
     * @author Jackpot
     * @date 2022/5/26 10:32
     */
    private ULog getAnnotation(JoinPoint joinPoint) {
        //获取当前请求方法对象
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        //返回对应的注解对象
        return method.getAnnotation(ULog.class);
    }

    /**
     * 获取当前的请求对象(并且缓存当前用户信息)
     *
     * @param sysLog 实体
     * @return javax.servlet.http.HttpServletRequest
     * @author Jackpot
     * @date 2022/5/26 10:33
     */
    private HttpServletRequest getCurrentRequest(SystemLog sysLog) {
        // 获取request
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        //从获取RequestAttributes中获取HttpServletRequest的信息
        assert requestAttributes != null;
        HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
        //获取请求中的cookie集合
        assert request != null;
        sysLog.setUserName(cacheHandler.getUserInfoCache(request).getUsername());
        //返回
        return request;
    }

    /**
     * 格式处理请求缓存和处理
     *
     * @param joinPoint      切面
     * @param uLogAnnotation 注解
     * @param sysLog         实体
     * @author Jackpot
     * @date 2022/5/26 10:33
     */
    private void formatParamEvent(JoinPoint joinPoint, ULog uLogAnnotation, SystemLog sysLog) {
        //判断当前是否应该缓存请求参数
        if (uLogAnnotation.isSaveRequestData()) {
            //获取当前请求参数对象
            try {
                //获取参数对象
                Object[] args = joinPoint.getArgs();
                //参数缓存
                formatRequestParam(args, sysLog);
            } catch (Exception e) {
                //请求参数处理异常
                log.error("请求参数处理异常", e);
            }
        }
    }

    /**
     * 格式化请求参数
     *
     * @param args   参数
     * @param sysLog 实体
     * @author Jackpot
     * @date 2022/5/26 10:35
     */
    private void formatRequestParam(Object[] args, SystemLog sysLog) {
        //定义一个集合缓存
        JSONArray paramArray = new JSONArray();
        //遍历获取每个参数对象
        for (Object arg : args) {
            //判断当前参数中是否存在无法转换的对象
            try {
                //判断是否可以转换
                Object value = JSON.toJSON(arg);
                //缓存
                paramArray.add(value);
            } catch (Exception e) {
                //参数无法转换的不处理
            }
        }
        //缓存对应的请求参数
        sysLog.setParam(paramArray);
    }

    /**
     * 格式处理响应参数的缓存和处理
     *
     * @param uLogAnnotation 切面
     * @param result         注解
     * @param sysLog         实体
     * @author Jackpot
     * @date 2022/5/26 10:34
     */
    private void formatResultEvent(ULog uLogAnnotation, Object result, SystemLog sysLog) {
        //判断当前的响应参数是否可以转换为json格式
        try {
            //请求结果
            JSONObject resultJson = JSON.parseObject(JSON.toJSONString(result));
            //获取响应状态
            Integer resultCode = resultJson.getInteger("code");
            //缓存本次请求的code
            sysLog.setCode(resultCode);
            //如果响应不成功 缓存对应的错误信息
            if (!CommonConstants.SYSTEM_SUCCESS_CODE.equals(resultCode)) {
                String msg = resultJson.getString("msg");
                String tip = resultJson.getString("tip");
                //缓存对应的错误信息
                sysLog.setException(!Strings.isNullOrEmpty(msg) ? msg :
                        !Strings.isNullOrEmpty(tip) ? tip : null);
            }
            //判断是否应该缓存响应
            if (uLogAnnotation.isSaveResponseData()) {
                //请求方法的响应结果
                sysLog.setResponseResult(resultJson);
            }
        } catch (Exception e) {
            //请求参数处理异常
            log.error("请求响应处理异常", e);
        }

    }

    @Data
    static class SavaLogBO {
        private JoinPoint joinPoint;
        private LocalDateTime beginTime;
        private LocalDateTime endTime;
        private Long runtime;
        private Object result;
        private String exceptionMsg;
    }
}

 

posted @ 2022-08-23 17:44  JackpotHan  阅读(199)  评论(0编辑  收藏  举报