zoukankan      html  css  js  c++  java
  • AOP-配合slf4j打印日志

    基本思想

    1. 凡在目标实例上或在目标实例方法(非静态方法)上标注自定义注解@AutoLog,其方法执行时将触发AOP操作;
    2. @AutoLog只有一个参数,用来控制是否打印该方法的参数和返回结果的json字符串,默认不打印,通过@AutoLog(true)开启
    3. 通过AOP拦截方法并打印日志

    代码

    package com.yan.mssm.aop;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface AutoLog {
        // log params and result when true
        boolean value() default false;
    }
    
    package com.yan.mssm.aop;
    
    import com.fasterxml.jackson.annotation.JsonAutoDetect;
    import com.fasterxml.jackson.annotation.PropertyAccessor;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.Signature;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.aop.framework.AdvisedSupport;
    import org.springframework.aop.framework.AopProxy;
    import org.springframework.aop.support.AopUtils;
    import org.springframework.stereotype.Component;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.el.MethodNotFoundException;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.util.*;
    import java.util.stream.Collectors;
    
    @Component
    @Aspect
    public class AutoLogAspectJ {
        private static final String PREFIX = "[";
        private static final String SUFFIX = "]";
        private static final String END = "end";
        private static final String BEGIN = "begin";
        private static final String PARAMS = "params";
        private static final String RESULT = "result";
        private static final String DELIMITER = " || ";
        private static final int STRING_MAX_LENGTH = 1 << 10;
        private static final Class<AutoLog> AUTO_LOG_CLASS = AutoLog.class;
        private static final String LOG_FORMAT = "[AOP-LOG]->Method {}: {}";
        private static final Class<RequestMapping> REQUEST_MAPPING_CLASS = RequestMapping.class;
        private static Logger LOGGER;
        private Method method;
        private String url;
        private Class<?> targetClass;
        private String methodSignature;
        private boolean debug;
    
        private static Object getCglibProxyTargetObject(Object proxy) {
            Field h;
            try {
                h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
                h.setAccessible(true);
                Object dynamicAdvisedInterceptor = h.get(proxy);
                Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
                advised.setAccessible(true);
                return ((AdvisedSupport) advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();
            } catch (Exception e) {
                return null;
            }
        }
    
        private static Object getJdkDynamicProxyTargetObject(Object proxy) {
            try {
                Field h = proxy.getClass().getSuperclass().getDeclaredField("h");
                h.setAccessible(true);
                AopProxy aopProxy = (AopProxy) h.get(proxy);
                Field advised = aopProxy.getClass().getDeclaredField("advised");
                advised.setAccessible(true);
                return ((AdvisedSupport) advised.get(aopProxy)).getTargetSource().getTarget();
            } catch (Exception e) {
                return null;
            }
        }
    
        private Object getTarget(Object proxy) {
            if (!AopUtils.isAopProxy(proxy)) {
                return proxy;
            }
            if (AopUtils.isJdkDynamicProxy(proxy)) {
                return getJdkDynamicProxyTargetObject(proxy);
            }
            return getCglibProxyTargetObject(proxy);
        }
    
        @Around(value = "@within(com.yan.mssm.aop.AutoLog) || @annotation(com.yan.mssm.aop.AutoLog)")
        public Object test(ProceedingJoinPoint point) throws Throwable {
            init(point);
            Optional.ofNullable(url).ifPresent(u -> LOGGER.info("[AOP-LOG]->Input URL:{}", u));
            LOGGER.info(LOG_FORMAT, BEGIN, methodSignature);
            printParams(point);
            Object result = point.proceed();
            printResult(result);
            LOGGER.info(LOG_FORMAT, END, methodSignature);
            return result;
        }
    
        private void init(ProceedingJoinPoint point) {
            Signature signature = point.getSignature();
            methodSignature = signature.toString();
            targetClass = point.getTarget().getClass();
            LOGGER = LoggerFactory.getLogger(targetClass);
            method = Arrays.stream(targetClass.getDeclaredMethods())
                    .filter(method -> method.toString().equals(signature.toLongString()))
                    .findFirst().orElseThrow(MethodNotFoundException::new);
            setUrl();
            debug = isDebug();
        }
    
        private void printResult(Object result) {
            if (!debug) {
                return;
            }
            Optional.ofNullable(result).ifPresent(r -> LOGGER.info(LOG_FORMAT, RESULT, r));
        }
    
        private void printParams(ProceedingJoinPoint point) {
            if (!debug) {
                return;
            }
            Object[] args = point.getArgs();
            if (args.length == 0) {
                return;
            }
            MethodSignature methodSignature = (MethodSignature) point.getSignature();
            String[] paramNames = methodSignature.getParameterNames();
            Map<String, Object> argMap = new LinkedHashMap<>();
            for (int i = 0; i < args.length; i++) {
                String argName = paramNames[i];
                argMap.put(argName, getTarget(args[i]));
            }
            String paramString = toJsonString(argMap);
            Optional.ofNullable(paramString)
                    .ifPresent(str -> LOGGER.info(LOG_FORMAT, PARAMS, str));
        }
    
        private boolean isDebug() {
            return (targetClass.isAnnotationPresent(AUTO_LOG_CLASS) && targetClass.getAnnotation(AUTO_LOG_CLASS).value())
                    || (method.isAnnotationPresent(AutoLog.class) && method.getAnnotation(AUTO_LOG_CLASS).value());
        }
    
        private void setUrl() {
            boolean controller = targetClass.isAnnotationPresent(RestController.class)
                    || targetClass.isAnnotationPresent(Controller.class);
    
            if (!controller) {
                url = null;
                return;
            }
    
            List<String> urlList = getUrlList();
            url = getCollect(urlList);
        }
    
        private List<String> getUrlList() {
            List<String> urlList = new ArrayList<>();
            String[] urls;
    
            boolean hasControllerMapping = hasControllerMapping();
            boolean hasMethodMapping = hasMethodMapping();
            if (!hasControllerMapping && !hasMethodMapping) {
                return urlList;
            }
    
            if (!hasControllerMapping) {
                urls = method.getAnnotation(REQUEST_MAPPING_CLASS).value();
                urlList.addAll(Arrays.asList(urls));
                return urlList;
            }
    
            if (!hasMethodMapping) {
                urls = targetClass.getAnnotation(REQUEST_MAPPING_CLASS).value();
                urlList.addAll(Arrays.asList(urls));
                return urlList;
            }
    
            Arrays.stream(targetClass.getAnnotation(REQUEST_MAPPING_CLASS).value())
                    .forEach(ctlrUrl ->
                            Arrays.stream(method.getAnnotation(REQUEST_MAPPING_CLASS).value())
                                    .forEach(methodUrl -> {
                                        StringBuilder sbd = new StringBuilder();
                                        urlList.add(sbd.append(ctlrUrl).append(methodUrl).toString());
                                    }));
            return urlList;
        }
    
        private boolean hasMethodMapping() {
            return method.isAnnotationPresent(REQUEST_MAPPING_CLASS)
                    && method.getAnnotation(REQUEST_MAPPING_CLASS).value().length > 0;
        }
    
        private boolean hasControllerMapping() {
            return targetClass.isAnnotationPresent(REQUEST_MAPPING_CLASS)
                    && targetClass.getAnnotation(REQUEST_MAPPING_CLASS).value().length > 0;
        }
    
        private String getCollect(List<String> urlList) {
            return urlList.stream().collect(Collectors.joining(DELIMITER, PREFIX, SUFFIX));
        }
    
        private String toJsonString(Object object) {
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
            String result = null;
            try {
                byte[] bytes = objectMapper.writeValueAsBytes(object);
                result = new String(bytes, "utf-8");
                result = result.length() > STRING_MAX_LENGTH ? null : result;
            } catch (Exception e) {
                LOGGER.error("AutoLogAspectJ.toJsonString exception:
    {}", e.getMessage());
            }
            return result;
        }
    }
    
  • 相关阅读:
    [CareerCup][Google Interview] Merge Two BST
    [面试] 全排列(非递归)
    [CareerCup][Google Interview] 打印最长序列
    [CareerCup][Google Interview] 实现一个具有get_min的Queue
    [LeetCode] Count and Say
    [LeetCode] Decode Ways
    定期向企业内部员工介绍企业当前的业务状况及未来的发展方向
    基础数据的来源的统一
    项目管理之代码合并
    年初离职潮的思考
  • 原文地址:https://www.cnblogs.com/yw0219/p/8627988.html
Copyright © 2011-2022 走看看