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:\n{}", e.getMessage());
        }
        return result;
    }
}
posted @ 2018-03-23 00:36  舒山  阅读(1688)  评论(0编辑  收藏  举报