• Spring++:AOP 拦截 Service | Controller 日志


    首先我们为什么需要做日志管理,在现实的上线中我们经常会遇到系统出现异常或者问题。

    这个时候就马上打开CRT或者SSH连上服务器拿日子来分析。受网络的各种限制。于是我们就想为什么不能直接在管理后台查看报错的信息呢。

    于是日志管理就出现了:↓

    引入相关依赖:

    <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>

    第一步定义两个注解:

    /**
     * 自定义注解 拦截service
     *
     */
    @Target({ElementType.PARAMETER, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface SystemServiceLog {
        String description() default "";
    }
    /**
     * 自定义注解 拦截Controller
     *
     */
    @Target({ElementType.PARAMETER, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface SystemControllerLog {
        String description() default "";
    }

    第二步创建一个切点类:

    package com.mi.mlq.aoplog;
    
    import com.alibaba.fastjson.JSON;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.core.NamedThreadLocal;
    import org.springframework.stereotype.Component;
    
    import java.lang.reflect.Method;
    import java.text.SimpleDateFormat;
    
    /**
     * 切点类
     *
     * @version 1.0
     * @since 2020年12月15日16:58:59
     */
    @Aspect
    @Component
    public class ServiceLogAspect {
    
        /**
         * 本地异常日志记录对象
         */
        private static final Logger LOGGER = LoggerFactory.getLogger(ServiceLogAspect.class);
        private static final ThreadLocal<Long> START_TIME_THREAD_LOCAL = new NamedThreadLocal<>("ThreadLocal StartTime");
    
        /**
         * Controller层切点
         */
        @Pointcut("@annotation(com.mi.mlq.aoplog.SystemServiceLog)")
        public void serviceAspect() {
        }
    
        /**
         * 前置通知 用于拦截Controller层记录用户的操作
         *
         * @param joinPoint 切点
         */
        @Before("serviceAspect()")
        public void doBefore(JoinPoint joinPoint) {
            // 线程绑定变量(该数据只有当前请求的线程可见)
            START_TIME_THREAD_LOCAL.set(System.currentTimeMillis());
            String classMethod = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
            String params = "";
            if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {
                for (int i = 0; i < joinPoint.getArgs().length; i++) {
                    params += JSON.toJSONString(joinPoint.getArgs()[i]) + ";";
                }
            }
            LOGGER.info("[" + classMethod + "]" + "[params={}]", params);
        }
    
        @AfterReturning(pointcut = "serviceAspect()")
        public void doAfterReturning(JoinPoint joinPoint) {
            // 1、得到线程绑定的局部变量(开始时间)
            long beginTime = START_TIME_THREAD_LOCAL.get();
            // 2、结束时间
            long endTime = System.currentTimeMillis();
            String classMethod = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
            LOGGER.info("[" + classMethod + "]" + "[计时结束:{}  耗时:{}ms 最大内存: {}m  已分配内存: {}m  已分配内存中的剩余空间: {}m  最大可用内存: {}m]",
                    new SimpleDateFormat("YYYY-MM-DD hh:mm:ss.SSS").format(endTime), (endTime - beginTime),
                    Runtime.getRuntime().maxMemory() / 1024 / 1024,
                    Runtime.getRuntime().totalMemory() / 1024 / 1024, Runtime.getRuntime().freeMemory() / 1024 / 1024,
                    (Runtime.getRuntime().maxMemory() - Runtime.getRuntime().totalMemory() + Runtime.getRuntime().freeMemory()) / 1024 / 1024);
        }
    
        /**
         * 获取注解中对方法的描述信息 用于service层注解
         *
         * @param joinPoint 切点
         * @return 方法描述
         * @throws Exception
         */
        public static String getServiceMthodDescription(JoinPoint joinPoint)
                throws Exception {
            String targetName = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            Object[] arguments = joinPoint.getArgs();
            Class targetClass = Class.forName(targetName);
            Method[] methods = targetClass.getMethods();
            String description = "";
            for (Method method : methods) {
                if (method.getName().equals(methodName)) {
                    Class[] clazzs = method.getParameterTypes();
                    if (clazzs.length == arguments.length) {
                        description = method.getAnnotation(SystemServiceLog.class).description();
                        break;
                    }
                }
            }
            return description;
        }
    
    }
    package com.mi.mlq.aoplog;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.core.NamedThreadLocal;
    import org.springframework.stereotype.Component;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    
    import javax.servlet.http.HttpServletRequest;
    import java.lang.reflect.Method;
    import java.text.SimpleDateFormat;
    import java.util.Enumeration;
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * 切点类
     *
     * @version 1.0
     * @since 2020年12月15日16:58:59
     */
    @Aspect
    @Component
    public class ControllerLogAspect {
    
        /**
         * 本地异常日志记录对象
         */
        private static final Logger LOGGER = LoggerFactory.getLogger(ControllerLogAspect.class);
        private static final ThreadLocal<Long> START_TIME_THREAD_LOCAL = new NamedThreadLocal<>("ThreadLocal StartTime");
    
        /**
         * Controller层切点
         */
        @Pointcut("@annotation(com.mi.mlq.aoplog.SystemControllerLog)")
        public void controllerAspect() {
        }
    
        /**
         * 前置通知 用于拦截Controller层记录用户的操作
         *
         * @param joinPoint 切点
         */
        @Before("controllerAspect()")
        public void doBefore(JoinPoint joinPoint) {
            // 线程绑定变量(该数据只有当前请求的线程可见)
            START_TIME_THREAD_LOCAL.set(System.currentTimeMillis());
            LOGGER.info("doBefore controller logs start...");
            String url = "";
            try {
                HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
                url = String.valueOf(request.getRequestURL());
                LOGGER.info("[requestURL]:{}", url);
                LOGGER.info(url + "[remoteAddr]:{}", request.getRemoteAddr());
                LOGGER.info(url + "[remoteHost]:{}", request.getRemoteHost());
                LOGGER.info(url + "[localAddr]:{}", request.getLocalAddr());
                LOGGER.info(url + "[method]:{}", request.getMethod());
                LOGGER.info(url + "[headers]:{}", getHeadersInfo(request));
                LOGGER.info(url + "[parameters]:{}", this.getParamMap(request));
                LOGGER.info(url + "[classMethod]{}", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
            } catch (Exception e) {
                LOGGER.error(url + "[doBefore controller error={}]", e);
            }
            LOGGER.info(url + "[doBefore controller logs end...]");
        }
    
        @AfterReturning(returning = "response", pointcut = "controllerAspect()")
        public void doAfterReturning(Object response) {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            String url = String.valueOf(request.getRequestURL());
            LOGGER.info(url + "[RESPONSE...]: {}", response);
            // 1、得到线程绑定的局部变量(开始时间)
            long beginTime = START_TIME_THREAD_LOCAL.get();
            // 2、结束时间
            long endTime = System.currentTimeMillis();
            LOGGER.info(url + "[计时结束:{}  耗时:{}ms  URI: {}  最大内存: {}m  已分配内存: {}m  已分配内存中的剩余空间: {}m  最大可用内存: {}m]",
                    new SimpleDateFormat("yyyy-mm-dd hh:mm:ss.SSS").format(endTime), (endTime - beginTime),
                    request.getRequestURI(), Runtime.getRuntime().maxMemory() / 1024 / 1024,
                    Runtime.getRuntime().totalMemory() / 1024 / 1024, Runtime.getRuntime().freeMemory() / 1024 / 1024,
                    (Runtime.getRuntime().maxMemory() - Runtime.getRuntime().totalMemory() + Runtime.getRuntime().freeMemory()) / 1024 / 1024);
        }
    
        private Map<String, String> getHeadersInfo(HttpServletRequest request) {
            Map<String, String> map = new HashMap<>();
            Enumeration<String> headerNames = request.getHeaderNames();
            while (headerNames.hasMoreElements()) {
                String key = headerNames.nextElement();
                String value = request.getHeader(key);
                map.put(key, value);
            }
            return map;
        }
    
        private Map<String, Object> getParamMap(HttpServletRequest request) {
            Map<String, Object> paramMap = new HashMap<String, Object>();
            Enumeration<?> paramNames = request.getParameterNames();
            while (paramNames.hasMoreElements()) {
                String key = (String) paramNames.nextElement();
                paramMap.put(key, request.getParameter(key));
            }
            return paramMap;
        }
    
        /**
         * 获取注解中对方法的描述信息 用于Controller层注解
         *
         * @param joinPoint 切点
         * @return 方法描述
         * @throws Exception
         */
        public static String getControllerMethodDescription(JoinPoint joinPoint) throws Exception {
            String targetName = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            Object[] arguments = joinPoint.getArgs();
            Class targetClass = Class.forName(targetName);
            Method[] methods = targetClass.getMethods();
            String description = "";
            for (Method method : methods) {
                if (method.getName().equals(methodName)) {
                    Class[] clazzs = method.getParameterTypes();
                    if (clazzs.length == arguments.length) {
                        description = method.getAnnotation(SystemControllerLog.class).description();
                        break;
                    }
                }
            }
            return description;
        }
    
    }

    使用方式:在对应的方法上加上此注解 即可享用!

  • 相关阅读:
    力扣(LeetCode)922. 按奇偶排序数组 II
    力扣(LeetCode)1002. 查找常用字符
    力扣(LeetCode)15. 三数之和
    Java == 和 equals 区别
    力扣(LeetCode)125. 验证回文串
    力扣(LeetCode) 905. 按奇偶排序数组
    力扣(LeetCode)832. 翻转图像
    力扣(LeetCode) 771. 宝石与石头
    Sticks
    荷马史诗
  • 原文地址:https://www.cnblogs.com/codingmode/p/15293068.html
Copyright © 2020-2023  润新知