SpringCloud中,自定义请求链路追踪之二三事

小弟这两天在部署springcloud项目,需要在日志中打印出请求相关信息,自定义微服务请求链路追踪,所以写下这篇文章。

首先明确,traceId在springcloud gateway中生成,然后向服务中传递。那么首先配置网关层。

一、springcloud gateway中

1、新建 TraceFilter (在authfilter(鉴权)执行前)

/**
 * traceId 过滤器
 */
@Order(-1)
@Slf4j
@Component
public class TraceFilter implements GlobalFilter {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String traceId = UUID.randomUUID().toString(true);
        //对请求对象request进行增强
        ServerHttpRequest req = request.mutate().headers(httpHeaders -> {
            //httpHeaders 封装了所有的请求头
            httpHeaders.add(Constant.REQUEST_ID, traceId);
            MDC.put(Constant.REQUEST_ID, traceId);
        }).build();
        log.info("request ------ request-id:[{}] uri:[{}]", traceId, req.getURI().getPath());
        exchange.mutate().request(req);
        return chain.filter(exchange);
    }
}

3、OpenFeign添加traceId全链路监控

因为authFilter通过openfeign远程调用鉴权服务,实现鉴权功能,所以也要将traceId通过openfeign传递到鉴权服务(放置在请求头上)

/**
 * 配置OpenFeign透传traceId
 */
@Component
public class OpenFeignRequestInterceptor implements RequestInterceptor {

        @Override
        public void apply(RequestTemplate requestTemplate) {
            String traceId = MDC.get(Constant.REQUEST_ID);
            requestTemplate.header(Constant.REQUEST_ID, traceId);
        }
}

因为通常更需要关注的是下游的多个服务间日志串联,所以gateway可写可不写入MDC,需要把生成的traceid传到下游就可以了,

二、springcloud服务

1、新建过滤器 TraceFilter

/**
 * 拦截traceId,并设置到MDC中
 */
@Order(0)
@Component
@Slf4j
public class TraceFilter implements Filter {
    /**
     * 目标方法执行前
     * 该方法在控制器处理请求方法前执行
     *
     * @param servletRequest  请求
     * @param servletResponse 响应
     * @param filterChain  过滤链
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        String requestId = ServletRequestUtils.getRequestId();
        MDC.put(Constant.REQUEST_ID, requestId);
        MDC.put(Constant.ACCOUNT, ServletRequestUtils.getCurrentLoginUserAccount());
        MDC.put(Constant.REQUEST_HEADER_CURRENT_USER, ServletRequestUtils.getCurrentUser());
        try {
            HttpServletRequest servletRequest1 = (HttpServletRequest) servletRequest;
            log.info("request start ------ request-id:[{}] account:[{}] uri:[{}]", requestId, ServletRequestUtils.getCurrentLoginUserAccount(),servletRequest1.getRequestURI());
            filterChain.doFilter(servletRequest, servletResponse);
            log.info("request finished ------ request-id:[{}] account:[{}] uri:[{}]", requestId, ServletRequestUtils.getCurrentLoginUserAccount(), servletRequest1.getRequestURI());
        } finally {
            MDC.clear();
        }
    }


}

注意:这里使用filter的原因是在方法调用前后可以加上自己想要的日志去标识,并且在方法执行完后手动清除MDC。

2、如果调用方法中,有通过openfeign调用其他服务,所以也要将traceId通过openfeign传递到其他服务(放置在请求头上)

/**
 * 配置OpenFeign透传traceId和当前用户
 */
@Component
public class OpenFeignRequestInterceptor implements RequestInterceptor {

        @Override
        public void apply(RequestTemplate requestTemplate) {
            String traceId = MDC.get(Constant.REQUEST_ID);
            String currentUser = MDC.get(Constant.REQUEST_HEADER_CURRENT_USER);
            //traceId
            requestTemplate.header(Constant.REQUEST_ID, traceId);
            //当前用户信息
            requestTemplate.header(Constant.REQUEST_HEADER_CURRENT_USER, currentUser);
        }
}

三、日志

小弟使用的默认的logback.xml

<!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{request-id}] [%X{account}]  [%-5level] %logger{50} - %msg%n
            </pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>

通过%X{request-id} 可将traceId打印在日志中,%X{account}将当前用户打印在日志中。

小弟发送一次请求,结果如下图所示:

网关服务:

2024-03-12 10:27:38.700 [reactor-http-nio-2] [25c5fbb1d28e401d8575a75ec54ae367] [INFO ] c.s.popper.huntergateway.filter.TraceFilter - request ------ request-id:[25c5fbb1d28e401d8575a75ec54ae367] uri:[/assetsSift/queryAssetsSiftStrategy]
鉴权服务:

2024-03-12 10:01:03.511 [http-nio-9002-exec-3] [25c5fbb1d28e401d8575a75ec54ae367] [hunterAdmin]  [INFO ] c.s.popper.huntercommonconfig.config.TraceFilter - request start ------ request-id:[25c5fbb1d28e401d8575a75ec54ae367] account:[hunterAdmin] uri:[/auth/checkToken]
2024-03-12 10:01:03.520 [http-nio-9002-exec-3] [25c5fbb1d28e401d8575a75ec54ae367] [hunterAdmin]  [INFO ] c.s.popper.hunteruser.controller.AuthController - 校验token
2024-03-12 10:01:03.550 [http-nio-9002-exec-3] [25c5fbb1d28e401d8575a75ec54ae367] [hunterAdmin]  [INFO ] c.s.popper.huntercommonconfig.config.TraceFilter - request finished ------ request-id:[25c5fbb1d28e401d8575a75ec54ae367] account:[hunterAdmin] uri:[/auth/checkToken]
业务服务:

2024-03-12 10:01:03.642 [http-nio-9003-exec-1] [25c5fbb1d28e401d8575a75ec54ae367] [lei] [INFO ] c.s.popper.huntercommonconfig.config.TraceFilter - request start ------ request-id:[25c5fbb1d28e401d8575a75ec54ae367] account:[lei] uri:[/assetsSift/queryAssetsSiftStrategy]
2024-03-12 10:01:03.680 [http-nio-9003-exec-1] [25c5fbb1d28e401d8575a75ec54ae367] [lei] [INFO ] c.s.p.h.controller.AssetsSiftStrategyController - queryAssetsSiftStrategy param:{}
2024-03-12 10:01:03.812 [http-nio-9003-exec-1] [25c5fbb1d28e401d8575a75ec54ae367] [lei] [INFO ] c.s.popper.huntercommonconfig.config.TraceFilter - request finished ------ request-id:[25c5fbb1d28e401d8575a75ec54ae367] account:[lei] uri:[/assetsSift/queryAssetsSiftStrategy]
备注:
  1. 各微服务均需要添加logbak.xml文件实现打印traceId。
  2. 各微服务间通过openfeign在header上添加traceId来进行传递。
  3. 各微服务均须添加traceFilter,来实现接收traceId并打印在日志中。

综上就可以实现通过一个traceId查询到全链路的日志了。

希望自己遇到的诸多困难,能带给各位一点点小小的帮助,感谢。

  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值