我是靠谱客的博主 要减肥蜗牛,最近开发中收集的这篇文章主要介绍Springcloud中Controller和Feign的日志切面实现,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

组件Feign自身的日志是debug级别并且日志行数太多,不符合我们精简日志的要求,故自己实现日志切面。

import com.alibaba.fastjson.JSON;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
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.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

@Aspect
@Slf4j
public class LogAspect {

    private final String PARENT_HTTP_REQUEST_TRACKING_ID = "B2cParentHttpRequestTrackingId";
    //private final String CURRENT_REQUEST_TRACKING_ID = "B2cCurrentRequestTrackingId";
    private final Integer MAX_LENGTH = 1500;

    @Pointcut("execution(* com.sf.pharma..start..*Controller.*(..))")
    public void controllerPointCut() {
    }

    @Around("controllerPointCut()")
    public Object controllerAround(ProceedingJoinPoint pjp) throws Throwable {
        long startTimeMillis = System.currentTimeMillis();
        //返回切入点的签名
        Signature sig = pjp.getSignature();
        if (!(sig instanceof MethodSignature)) {
            log.error("该日志切面只能用于方法!");
            return pjp.proceed();
        }
        //获取全类名和方法名
        String className = pjp.getSignature().getDeclaringTypeName();
        String methodName = pjp.getSignature().getName();
        //
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
        HttpServletRequest httpRequest = servletRequestAttributes.getRequest();
        if (this.isBlackListUrl(httpRequest.getRequestURI())) {
            return pjp.proceed();
        }

        //Controller类的方法请求参数全部打印,不截取。
        Object resultObj = null;
        String errorMsg = null;
        String parentRequestId = UUID.randomUUID().toString();
        String currentRequestId = UUID.randomUUID().toString();
        try {
            //简单请求链路跟踪
            httpRequest.setAttribute(PARENT_HTTP_REQUEST_TRACKING_ID, parentRequestId);

            StringBuilder reqContentSb = new StringBuilder();
            reqContentSb.append("RequestId:").append(currentRequestId)
                .append(", ").append("ParentRequestId:").append(parentRequestId)
                .append(", Request-").append(httpRequest.getMethod()).append("-Url:").append(httpRequest.getRequestURI())
                .append(", ").append("RequestClass:").append(className)
                .append(", ").append("RequestMethod:").append(methodName)
                .append(", ").append("RequestHeadParams:").append(StringUtil.subStr(buildReqHeaderLog(httpRequest), MAX_LENGTH))
                .append(", ").append("RequestParams:").append(buildReqParamsLog(pjp));
            log.info(reqContentSb.toString());
            resultObj = pjp.proceed();
        } catch (Throwable t) {
            errorMsg = t.toString();
            throw t;
        } finally {
            StringBuilder respContentSb = new StringBuilder();
            respContentSb.append("ResponseId:").append(currentRequestId)
                .append(", ").append("ParentResponseId:").append(parentRequestId)
                .append(", ").append("ResponseTime:").append(System.currentTimeMillis() - startTimeMillis).append("ms")
                .append(", ").append("ResponseResult:").append(StringUtil.subStr(JSON.toJSONString(resultObj), MAX_LENGTH));
            if (StringUtils.isNotBlank(errorMsg)) {
                respContentSb.append(", ").append("ResponseErrorMsg:").append(errorMsg);
            }
            log.info(respContentSb.toString());
        }
        return resultObj;
    }

    /**
     * 这个切面粒度太粗,会把调用下游接口的日志也打印出来。
     */
    @Pointcut("execution(* com.sf.pharma..api.facade..*Facade.*(..))")
    public void feignPointCut() {
    }

    @Around("feignPointCut()")
    public Object feignAround(ProceedingJoinPoint pjp) throws Throwable {
        long startTimeMillis = System.currentTimeMillis();
        //返回切入点的签名
        Signature sig = pjp.getSignature();
        if (!(sig instanceof MethodSignature)) {
            log.error("该日志切面只能用于方法!");
            return pjp.proceed();
        }
        //获取全类名和方法名
        String className = pjp.getSignature().getDeclaringTypeName();
        String methodName = pjp.getSignature().getName();
        //
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
        HttpServletRequest httpRequest = servletRequestAttributes.getRequest();
        if (this.isBlackListUrl(httpRequest.getRequestURI())) {
            return pjp.proceed();
        }

        //Feign类的方法请求参数太长时截取。
        Object resultObj = null;
        String errorMsg = null;
        String parentRequestId = null;
        String currentRequestId = UUID.randomUUID().toString();
        try {
            //简单请求链路跟踪
            parentRequestId = (String) httpRequest.getAttribute(PARENT_HTTP_REQUEST_TRACKING_ID);
            if (StringUtil.isEmpty(parentRequestId)) {
                parentRequestId = UUID.randomUUID().toString();
                httpRequest.setAttribute(PARENT_HTTP_REQUEST_TRACKING_ID, parentRequestId);
            }

            StringBuilder reqContentSb = new StringBuilder();
            reqContentSb.append("RequestId:").append(currentRequestId)
                .append(", ").append("ParentRequestId:").append(parentRequestId)
                .append(", ").append("RequestClass:").append(className)
                .append(", ").append("RequestMethod:").append(methodName)
                .append(", ").append("RequestParams:").append(StringUtil.subStr(buildReqParamsLog(pjp), MAX_LENGTH));
            log.info(reqContentSb.toString());
            resultObj = pjp.proceed();
        } catch (Throwable t) {
            errorMsg = t.toString();
            throw t;
        } finally {
            StringBuilder respContentSb = new StringBuilder();
            respContentSb.append("ResponseId:").append(currentRequestId)
                .append(", ").append("ParentResponseId:").append(parentRequestId)
                .append(", ").append("ResponseTime:").append(System.currentTimeMillis() - startTimeMillis).append("ms")
                .append(", ").append("ResponseResult:").append(StringUtil.subStr(JSON.toJSONString(resultObj), MAX_LENGTH));
            if (StringUtils.isNotBlank(errorMsg)) {
                respContentSb.append(", ").append("ResponseErrorMsg:").append(errorMsg);
            }
            log.info(respContentSb.toString());
        }
        return resultObj;
    }

    private boolean isBlackListUrl(String requestURI) {
        //一些url无需打印日志,可以排除一部分。
        String[] blackAry = new String[]{"/glhealthcheck/status/1", "/glhealthcheck/status/0"};
        for (int i = 0; i < blackAry.length; i++) {
            if (requestURI.endsWith(blackAry[i])) {
                return true;
            }
        }
        return false;
    }

    private String buildReqParamsLog(ProceedingJoinPoint pjp) {
        //返回被通知方法参数列表
        Object[] args = pjp.getArgs();
        Stream<?> stream = ArrayUtils.isEmpty(args) ? Stream.empty() : Arrays.asList(args).stream();
        //序列化时过滤掉request和response
        List<Object> reqArgs = stream
            .filter(arg -> (!(arg instanceof HttpServletRequest)
                && !(arg instanceof HttpServletResponse)
                && !(arg instanceof MultipartFile)))
            .collect(Collectors.toList());
        return JSON.toJSONString(reqArgs);
    }

    private String buildReqHeaderLog(HttpServletRequest httpRequest) {
        HashMap headerMap = new HashMap(16);
        Enumeration<String> headerNames = httpRequest.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String headerKey = headerNames.nextElement();
            String headerValue = httpRequest.getHeader(headerKey);
            //一些head参数对我们无用,可以排除一部分。
            //保留"clientId"、"Content-Length"、"Content-Type"、"Cookie"、"User-Agent"、"Host", "origin", "referer"、
            //保留"x-real-ip"、"x-forwarded-for"、
            String[] blackAry = new String[]{"x-cat-trace", "x-rontgen", "x-service-chain", "postman-token",
                "sec-ch-ua", "sec-ch-ua-mobile", "sec-ch-ua-platform", "sec-fetch-dest", "sec-fetch-mode", "sec-fetch-site",
                "accept", "accept-encoding", "accept-language", "connection", "cache-control", "scope"};
            List<String> blackList = Arrays.asList(blackAry);
            if (blackList.contains(headerKey)) {
                continue;
            }
            headerMap.put(headerKey, headerValue);
        }
        return JSON.toJSONString(headerMap);
    }


}

最后

以上就是要减肥蜗牛为你收集整理的Springcloud中Controller和Feign的日志切面实现的全部内容,希望文章能够帮你解决Springcloud中Controller和Feign的日志切面实现所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(61)

评论列表共有 0 条评论

立即
投稿
返回
顶部