• 日志切面思路(ruoyivue)


    日志持久化模块的设计思路:

    1. 为什么设计日志持久化模块?

    日志持久化到mysql可以更方便,快捷的让开发,运维人员定位到错误;

    2. 日志持久化功能设计的大概思路

    参照目前比较流行的ruoyi-vue后台管理框架,采用注解,切面的方式,将日志持久化到msql

    3. 日志持久化的内容(表的设计)

    4. 代码分析

    自定义注解

    package com.ruoyi.common.annotation;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    import com.ruoyi.common.enums.BusinessType;
    import com.ruoyi.common.enums.OperatorType;
    
    /**
     * 自定义操作日志记录注解
     * 
     * @author ruoyi
     *
     */
    @Target({ ElementType.PARAMETER, ElementType.METHOD })
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Log
    {
        /**
         * 模块 
         */
        public String title() default "";
    
        /**
         * 功能
         */
        public BusinessType businessType() default BusinessType.OTHER;
    
        /**
         * 操作人类别
         */
        public OperatorType operatorType() default OperatorType.MANAGE;
    
        /**
         * 是否保存请求的参数
         */
        public boolean isSaveRequestData() default true;
    
        /**
         * 是否保存响应的参数
         */
        public boolean isSaveResponseData() default true;
    }
    
    

    切面

    package com.ruoyi.framework.aspectj;
    
    import java.util.Collection;
    import java.util.Map;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.AfterThrowing;
    import org.aspectj.lang.annotation.Aspect;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    import org.springframework.validation.BindingResult;
    import org.springframework.web.multipart.MultipartFile;
    import org.springframework.web.servlet.HandlerMapping;
    import com.alibaba.fastjson.JSON;
    import com.ruoyi.common.annotation.Log;
    import com.ruoyi.common.core.domain.model.LoginUser;
    import com.ruoyi.common.enums.BusinessStatus;
    import com.ruoyi.common.enums.HttpMethod;
    import com.ruoyi.common.utils.ServletUtils;
    import com.ruoyi.common.utils.StringUtils;
    import com.ruoyi.common.utils.ip.IpUtils;
    import com.ruoyi.common.utils.SecurityUtils;
    import com.ruoyi.framework.manager.AsyncManager;
    import com.ruoyi.framework.manager.factory.AsyncFactory;
    import com.ruoyi.system.domain.SysOperLog;
    
    /**
     * 操作日志记录处理
     * 
     * @author ruoyi
     */
    @Aspect
    @Component
    public class LogAspect
    {
        private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
    
        /**
         * 处理完请求后执行
         *
         * @param joinPoint 切点
         */
        @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
        public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult)
        {
            handleLog(joinPoint, controllerLog, null, jsonResult);
        }
    
        /**
         * 拦截异常操作
         * 
         * @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(ServletUtils.getRequest());
                operLog.setOperIp(ip);
                operLog.setOperUrl(ServletUtils.getRequest().getRequestURI());
    //            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);
                // 保存数据库
                AsyncManager.me().execute(AsyncFactory.recordOper(operLog));
            }
            catch (Exception exp)
            {
                // 记录本地异常日志
                log.error("==前置通知异常==");
                log.error("异常信息:{}", exp.getMessage());
                exp.printStackTrace();
            }
        }
    
        /**
         * 获取注解中对方法的描述信息 用于Controller层注解
         * 
         * @param log 日志
         * @param operLog 操作日志
         * @throws Exception
         */
        public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog, Object jsonResult) throws Exception
        {
            // 设置action动作
            operLog.setBusinessType(log.businessType().ordinal());
            // 设置标题
            operLog.setTitle(log.title());
            // 设置操作人类别
            operLog.setOperatorType(log.operatorType().ordinal());
            // 是否需要保存request,参数和值
            if (log.isSaveRequestData())
            {
                // 获取参数的信息,传入到数据库中。
                setRequestValue(joinPoint, operLog);
            }
            // 是否需要保存response,参数和值
            if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult))
            {
                operLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, 2000));
            }
        }
    
        /**
         * 获取请求的参数,放到log中
         * 
         * @param operLog 操作日志
         * @throws Exception 异常
         */
        private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog) throws Exception
        {
            String requestMethod = operLog.getRequestMethod();
            if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod))
            {
                String params = argsArrayToString(joinPoint.getArgs());
                operLog.setOperParam(StringUtils.substring(params, 0, 2000));
            }
            else
            {
                Map<?, ?> paramsMap = (Map<?, ?>) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
                operLog.setOperParam(StringUtils.substring(paramsMap.toString(), 0, 2000));
            }
        }
    
        /**
         * 参数拼装
         */
        private String argsArrayToString(Object[] paramsArray)
        {
            String params = "";
            if (paramsArray != null && paramsArray.length > 0)
            {
                for (Object o : paramsArray)
                {
                    if (StringUtils.isNotNull(o) && !isFilterObject(o))
                    {
                        try
                        {
                            Object jsonObj = JSON.toJSON(o);
                            params += jsonObj.toString() + " ";
                        }
                        catch (Exception e)
                        {
                        }
                    }
                }
            }
            return params.trim();
        }
    
        /**
         * 判断是否需要过滤的对象。
         * 
         * @param o 对象信息。
         * @return 如果是需要过滤的对象,则返回true;否则返回false。
         */
        @SuppressWarnings("rawtypes")
        public boolean isFilterObject(final Object o)
        {
            Class<?> clazz = o.getClass();
            if (clazz.isArray())
            {
                return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
            }
            else if (Collection.class.isAssignableFrom(clazz))
            {
                Collection collection = (Collection) o;
                for (Object value : collection)
                {
                    return value instanceof MultipartFile;
                }
            }
            else if (Map.class.isAssignableFrom(clazz))
            {
                Map map = (Map) o;
                for (Object value : map.entrySet())
                {
                    Map.Entry entry = (Map.Entry) value;
                    return entry.getValue() instanceof MultipartFile;
                }
            }
            return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
                    || o instanceof BindingResult;
        }
    }
    
    
    
    

    5. 使用方式

    6. 数据示范

  • 相关阅读:
    numpy的shuffle函数
    特征值、特征向量
    keras的Embedding层
    自己写着玩的一个天气APP
    使用mbed进行STM32板子的开发
    提高ListView的效率
    自定义ListView里面的Item的内容
    Android控件使用自定义字体
    使用Handler类来更新UI
    MongoDB在Java下的增删查改
  • 原文地址:https://www.cnblogs.com/wuliqqq/p/15983842.html
Copyright © 2020-2023  润新知