AOP实现日志打印

  • 使用环绕通知打印日志
  • 获取request请求

 HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();


import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import feign.form.ContentType;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.*;

@Aspect
@Component
@Slf4j
public class RequestLogAspect {

    private static final String PATH = "path";

    private static final String METHOD = "method";

    private static final String REQUEST_BODY = "body";

    private static final String HEADER = "header";

    private static final String TIME = "time";

    @Pointcut(value = "execution(* com.*.controller..*.*(..))")
    public void point() {
    }

    @Around("point()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        HashMap<String, String> map = new HashMap<>();
        map.put(TIME, System.currentTimeMillis() + "");
        Object proceed = null;
        Throwable throwable = null;
        try {
            proceed = joinPoint.proceed();
        } catch (UndeclaredThrowableException e) {
            if (e.getUndeclaredThrowable() instanceof FlowException) {
                MethodSignature signature = (MethodSignature) joinPoint.getSignature();
                Method method = signature.getMethod();
                if (method.getReturnType().isAssignableFrom(Result.class)) {
                    proceed = Result.failure(ExceptionCode.TOO_BUSY);
                } else if (method.getReturnType().isAssignableFrom(Result.class)) {
                    proceed = Result.failure(ExceptionCode.TOO_BUSY);
                } else {
                    throw new BaseException(ExceptionCode.TOO_BUSY);
                }
            } else {
                log.warn("around exception joinPoint:[{}]",joinPoint,e);
            }
        } catch (Throwable e) {
            //解析异常,得到已知异常的转换返回
            proceed = GlobalExceptionHandler.exceptionHandler(e);
            throwable = e;
        } finally {
            try {
                decorateRequest(map, joinPoint);
                log(map, JSON.toJSONString(proceed));
            } catch (Exception e) {
                log.warn("around exception joinPoint:[{}]",joinPoint,e);
                WarnSupport.sendDingding("日志打印异常,异常信息:" + e.getMessage());
            }
        }
        //如果有异常则抛出异常,没有异常正常返回
        if (null != throwable) throw throwable;
        return proceed;
    }

    /**
     * 日志打印
     */
    private void log(Map<String, String> request, String responseBody) {
        log.info("\n<Mapping>" +
                        "\npath     -> {}" +
                        "\ntime     -> {}" +
                        "\nmethod   -> {}" +
                        "\nheaders  -> {}" +
                        "\nrequest  -> {}" +
                        "\nresponse -> {}",
                request.get(PATH),
                (System.currentTimeMillis() - Long.parseLong(request.get(TIME))) + "ms",
                request.get(METHOD),
                request.get(HEADER),
                request.get(REQUEST_BODY),
                responseBody
        );
    }

    /**
     * 解析请求
     */
    private void decorateRequest(Map<String, String> map, ProceedingJoinPoint joinPoint) {
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        map.put(PATH, request.getQueryString() != null ? String.format("%s?%s", request.getRequestURI(), request.getQueryString()) : request.getRequestURI());
        map.put(HEADER, builderHeaders(request));
        map.put(METHOD, request.getMethod());
        map.put(REQUEST_BODY, builderRequestParams(request, joinPoint.getArgs()));
    }

    /**
     * 构建请求头打印
     */
    private String builderHeaders(HttpServletRequest request) {
        Enumeration<String> headerNames = request.getHeaderNames();
        StringBuilder headers = new StringBuilder();
        while (headerNames.hasMoreElements()) {
            String key = headerNames.nextElement();
            String value = request.getHeader(key);
            headers.append(key).append(":").append(value)
                    .append(headerNames.hasMoreElements() ? "|" : "");
        }
        return headers.toString();
    }


    /**
     * 构建请求参数字符串
     */
    public static String builderRequestParams(HttpServletRequest request, Object[] args) {
        if (request.getContentType() == null) {
            return request.getQueryString();
        }

        if (request.getContentType().startsWith("application/json")) {
            StringBuilder stringBuilder = new StringBuilder();
            Arrays.stream(args)
                    .filter(it -> !(it instanceof ServletRequest))
                    .forEach(it -> stringBuilder.append(JSON.toJSONString(it)));
            return stringBuilder.toString();
        }

        if (request.getContentType().startsWith(ContentType.URLENCODED.getHeader())) {
            StringBuilder sb = new StringBuilder();
            Enumeration<String> parameterNames = request.getParameterNames();
            while (parameterNames.hasMoreElements()) {
                String name = parameterNames.nextElement();
                sb.append(name).append("=").append(request.getParameter(name)).append("&");
            }
            String result = sb.toString();
            return StringUtils.isBlank(result) ? result : result.substring(0, result.length() - 1);
        }

        if (isBinaryContent(request) && request.getContentType().startsWith(ContentType.MULTIPART.getHeader())) {
            return "file";
        }
        return null;
    }

    private static boolean isBinaryContent(final HttpServletRequest request) {
        if (request.getContentType() == null) {
            return false;
        }
        //文件上传不记录请求参数
        if (Objects.equals(request.getRequestURI(),"/file/upload")){
            return true;
        }
        return request.getContentType().startsWith("image")
                || request.getContentType().startsWith("video")
                || request.getContentType().startsWith("audio");
    }

}

上一篇:AOP实现注解,改变Controller返回参数


下一篇:Spring源码系列(九)——AOP动态代理源码分析