欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

spring cloud中实现日志追踪

程序员文章站 2022-03-05 08:08:53
...
  • 网关中设置filter对每一个请求的header中进行设置traceID(用于追踪每一个请求),并将traceSort(用于对后续请求进行排序)存入redis中
  • 在其余微服务中添加AOP对需要追踪的进行日志搜集

部分代码示例

package com.zy.zygateway.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import com.zy.core.model.RedisKey;
import com.zy.zygateway.service.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;

import java.util.UUID;

/**
 * 〈为每一个请求添加traceid〉
 */
@Component
@Slf4j
public class TraceIDFilter extends ZuulFilter {

    @Autowired
    private RedisService redisService;

    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        // 每一个请求都必须拦截
        return true;
    }

    @Override
    public Object run() throws ZuulException {

        // 为每一个请求添加traceid
        String traceId = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
        String traceSort = new Integer(0).toString();

        RequestContext requestContext = RequestContext.getCurrentContext();

        requestContext.addZuulRequestHeader("Trace-Id", traceId);
//        requestContext.addZuulRequestHeader("Request-Sort", traceSort);
        redisService.set(RedisKey.LOG.OPERATIONLOG + traceId, traceSort, 20L);
        log.info("请求【{}】,进入TraceIDFilter,成功分配Trace-Id【{}】",
                requestContext.getRequest().getRequestURL(),
                traceId);
        return null;
    }
}
package com.zy.zypayservice.config;

import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariableDefault;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
import org.springframework.util.StringUtils;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@Configuration
public class HystrixCredentialsContext {

    private static final HystrixRequestVariableDefault<Authentication> authentication = new HystrixRequestVariableDefault<>();

    private static final HystrixRequestVariableDefault<HttpServletRequest> httpRequest = new HystrixRequestVariableDefault<>();

    public static HystrixRequestVariableDefault<Authentication> getInstance() {
        return authentication;
    }

    public static HystrixRequestVariableDefault<HttpServletRequest> getHttpRequestInstance() {
        return httpRequest;
    }

    @Bean
    public RequestInterceptor requestTokenBearerInterceptor() {

        return new RequestInterceptor() {
            @Override
            public void apply(RequestTemplate requestTemplate) {
                Authentication auth = HystrixCredentialsContext.getInstance().get();
                HttpServletRequest request = HystrixCredentialsContext.getHttpRequestInstance().get();
                if (auth != null) {
                    requestTemplate.header("Trace-Id",
                            request.getHeader("Trace-Id"));
//                    requestTemplate.header("Request-Sort",
//                            String.valueOf(Integer.valueOf(request.getHeader("Request-Sort")).intValue() + 1));
                    if (!StringUtils.isEmpty(auth.getPrincipal()) && !auth.getPrincipal().toString().equals("anonymousUser")) {
                        if (auth instanceof OAuth2Authentication && auth.getDetails() instanceof OAuth2AuthenticationDetails) {
                            requestTemplate.header("Authorization",
                                    "bearer " + ((OAuth2AuthenticationDetails) auth.getDetails()).getTokenValue());

                        } else {
                            requestTemplate.header("Authorization", "");
                        }
                    } else {
                        requestTemplate.header("Authorization", "");
                    }
                }
            }
        };

    }

    @Bean
    public FilterRegistrationBean hystrixFilter() {

        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(new Filter() {

            @Override
            public void init(FilterConfig filterConfig) throws ServletException {
            }

            @Override
            public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                    throws IOException, ServletException {

                HystrixRequestContext.initializeContext();
                SecurityContext securityContext = SecurityContextHolder.getContext();
                if (securityContext != null) {
                    Authentication auth = (Authentication) securityContext.getAuthentication();
                    HystrixCredentialsContext.getInstance().set(auth);
                }
                chain.doFilter(request, response);

            }

            @Override
            public void destroy() {

            }

        });

        filterRegistrationBean.addUrlPatterns("/*");
        return filterRegistrationBean;

    }
}

package com.zy.zypayservice.aop;

import com.alibaba.fastjson.JSONObject;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext;
import com.zy.api.client.ZyRemoteClient.log.ZyHistoryRecordClient;
import com.zy.common.log.ZYLoggerManage;
import com.zy.core.annotation.ZyBizAnnBean;
import com.zy.core.annotation.ZyBizAnnotation;
import com.zy.core.model.BDic;
import com.zy.core.model.RedisKey;
import com.zy.model.log.in.AddZyHistoryRecordIn;
import com.zy.model.utils.ZyUserUtil;
import com.zy.zypayservice.config.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
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 javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.concurrent.CompletableFuture;

@Component
@Aspect
@Slf4j
public class RsetAPIAspectj {

    @Autowired
    private ZyHistoryRecordClient zyHistoryRecordClient;

    private static final ThreadLocal<AddZyHistoryRecordIn> paramThreadLocal = new NamedThreadLocal<AddZyHistoryRecordIn>("Param beginTime");

    @Value("${spring.application.name}")
    private String projectName;

    @Autowired
    private RedisService redisService;

    @Pointcut("execution(* com.zy.zypayservice.controller..*.*(..)) || execution(* com.zy.core.base.ZyBaseController.exceptionHandler())")
    public void resetAPIPointcut() {}

    /**
     * <方法执行之前>
     *
     * @param jp
     * @return void
     * @date 2019/1/3 10:22
     */
    @Before(value = "resetAPIPointcut()")
    public void restAPIBefore(JoinPoint jp) {
        String httpReqParam = null;
        String ip = null;

        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        StringBuilder sb = new StringBuilder();
        try {
            AddZyHistoryRecordIn in = new AddZyHistoryRecordIn();

            for (Object obj : jp.getArgs()) {
                if (obj instanceof HttpServletRequest) {
                    in.setParams(JSONObject.toJSONString(((HttpServletRequest) obj).getParameterMap()));
                } else if (obj instanceof HttpServletResponse) {

                } else if (obj instanceof Throwable) {
                    in.setException(obj.getClass().getName());
                    in.setExceptionDescribe(((Throwable) obj).getMessage());
                    in.setType(BDic.LOG_LEVEL.ERROR);
                } else {
                    ip = "ip:[" + request.getRemoteAddr() + "]";
                    httpReqParam = JSONObject.toJSONString(obj);

                    in.setParams(httpReqParam);
                    sb.append(ip).append(httpReqParam);
                    sb.append(JSONObject.toJSONString(obj));
                }
            }

            // 获取方法中注释内容
            ZyBizAnnBean mthodRemark = getMthodRemark(jp);
            // 获取追踪ID
            String traceId = request.getHeader("Trace-Id");
            // 获取请求次序号
            Integer requestSort = 0;
            boolean flag = true;
            do {
                Object obj = null;
                try {
                    obj = redisService.get(RedisKey.LOG.OPERATIONLOG + traceId);
                    if (obj != null) {
                        requestSort = Integer.valueOf(String.valueOf(obj));
                        redisService.set(RedisKey.LOG.OPERATIONLOG + traceId,
                                requestSort + 1,
                                200L);
                        flag = false;
                    }
                } catch (Exception e) {
                    flag = false;
                }
            } while (flag);
            // 判断是否有 ZyBizAnnBean 注解,有:执行历史记录 没有:不执行
            if (mthodRemark != null) {
                in.setTraceId(traceId);
                in.setProjectName(projectName);
                in.setSort(requestSort);
                String account = "";
                if (ZyUserUtil.getLoginUser() == null) {
                    account = "匿名";
                } else {
                    account = ZyUserUtil.getLoginUser().getAccount();
                }
                in.setAccount(account);
                in.setContent(mthodRemark.getOption());
                in.setSource(BDic.LOG_MODULE.BACK_MANAGER);
                in.setUrl(request.getRequestURL().toString());
                in.setIp(request.getRemoteAddr());
                in.setType(BDic.LOG_LEVEL.INFO);
                in.setUrlType(request.getMethod());
                in.setMethod(jp.getSignature().getName());
                in.setClassname(jp.getTarget().getClass().getName());
                in.setCreateTime(new Date());
                // 将请求参数存储到本地线程中
                paramThreadLocal.set(in);
            }
        }
        catch(Exception e) {
            log.error("AOP异常【{}】" + e.getMessage());
        } finally {
            log.info("====调用【{}】方法-开始:{}", jp.getSignature().toString(), sb);
        }
    }

    /**
     * <方法执行完成之后>
     *
     * @param jp
     * @param returnVal
     * @return void
     * @date 2019/1/3 10:21
     */
    @AfterReturning(pointcut = "resetAPIPointcut()", returning = "returnVal")
    public void restAPIAfter(JoinPoint jp, Object returnVal) {
        AddZyHistoryRecordIn addZyHistoryRecordIn = paramThreadLocal.get();
        StringBuilder sb = new StringBuilder();
        try {
            if (addZyHistoryRecordIn != null) {

                addZyHistoryRecordIn.setResParams(JSONObject.toJSONString(returnVal));
                addZyHistoryRecordIn.setEndTime(new Date());

                CompletableFuture.runAsync(() -> {
                    HystrixRequestContext context = HystrixRequestContext.initializeContext();
                    try {
                        zyHistoryRecordClient.addHistory(addZyHistoryRecordIn);
                    } catch (Exception e2) {
                        e2.printStackTrace();
                    } finally {
                        context.shutdown();
                    }
                });
            }
        } catch(Exception e) {
            log.error("AOP异常【{}】" + e.getMessage());
        } finally {
            sb.append(JSONObject.toJSONString(returnVal));
            log.info("====调用【{}】方法-开始:{}", jp.getSignature().toString(), sb);
        }
    }

    /**
     * <方法抛出异常>
     *
     * @param joinPoint
     * @param e
     * @return void
     * @date 2018/12/29 11:27
     */
    @AfterThrowing(pointcut = "resetAPIPointcut()", throwing = "e")
    public void restAPIAfterThrow(JoinPoint joinPoint, Throwable e) {

        try {
            AddZyHistoryRecordIn addZyHistoryRecordIn = paramThreadLocal.get();
            addZyHistoryRecordIn.setType(BDic.LOG_LEVEL.ERROR);
            addZyHistoryRecordIn.setException(e.toString());
            addZyHistoryRecordIn.setEndTime(new Date());
            zyHistoryRecordClient.addHistory(addZyHistoryRecordIn);
            CompletableFuture.runAsync(() -> {
                try {
                    HystrixRequestContext.initializeContext();
                    zyHistoryRecordClient.addHistory(addZyHistoryRecordIn);
                } catch (Exception e2) {
                    e2.printStackTrace();
                }
            });
        } catch (Exception e1) {
            ZYLoggerManage.error(this.getClass(), "后置异常日志拦截异常", e1);
        }
    }

    /**
     * <获取方法的注释信息>
     *
     * @param joinPoint
     * @return com.zy.core.annotation.ZyBizAnnBean
     * @date 2018/12/28 16:16
     */
    public ZyBizAnnBean getMthodRemark(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[] method = targetClass.getMethods();
        ZyBizAnnBean zab = null;
        for (Method m : method)
        {
            if (m.getName().equals(methodName))
            {
                Class[] tmpCs = m.getParameterTypes();
                if (tmpCs.length == arguments.length)
                {
                    ZyBizAnnotation methodCache = m.getAnnotation(ZyBizAnnotation.class);
                    if (methodCache != null)
                    {
                        String option = methodCache.option();
                        String name = methodCache.name();
                        zab = new ZyBizAnnBean();
                        zab.setName(name);
                        zab.setOption(option);
                        return zab;
                    }
                }
            }
        }
        return zab;
    }
}