分布式链路跟踪 traceId spanId parentSpanId自定义业务处理:比如设置userId

背景:在分布式服务架构下,一个 Web 请求从网关流入,有可能会调用多个服务对请求进行处理,拿到最终结果。在这个过程中每个服务之间的通信又是单独的网络请求,无论请求流经的哪个服务除了故障或者处理过慢都会对前端造成影响。

一、相关概念

在分布式链路追踪中有两个重要的概念:跟踪(trace)和 跨度(span)。trace 是请求在分布式系统中的整个链路视图,span 则代表整个链路中不同服务内部的视图,span 组合在一起就是整个 trace 的视图。

traceId:用于标识某一次具体的请求ID。当用户的请求进入系统后,会在RPC调用网络的第一层生成一个全局唯一的traceId,并且会随着每一层的RPC调用,不断往后传递,这样的话通过traceId就可以把一次用户请求在系统中调用的路径串联起来。

spanId,用于标识一次RPC调用在分布式请求中的位置。请求到达每个服务后,服务都会为请求生成spanId。当用户的请求进入系统后,处在RPC调用网络的第一层A时spanId初始值是0,进入下一层RPC调用B的时候spanId是0.1,继续进入下一层RPC调用C时spanId是0.1.1,而与B处在同一层的RPC调用D的spanId是0.2,这样的话通过spanId就可以定位某一次RPC请求在系统调用中所处的位置,以及它的上下游依赖分别是谁。

parent-spanId:用于标识上游RPC调用在分布式请求中的位置。请求到达每个服务后,随请求一起从上游传过来的上游服务的 spanId 会被记录成 parent-spanId,或者叫 pspanId。当前服务生成的 spanId 随着请求一起,在传到下游服务时,这个 spanId 又会被下游服务当作 parent-spanId 记录。

MDC:(Mapped Diagnostic Context)映射诊断环境,是 log4j 和 logback 提供的一种方便在线多线程条件下记录日志的功能,可以看成是一个与当前线程绑定的 ThreadLocal。

org.slf4j 1.7.25 版本
public static void put(String key, String val) throws IllegalArgumentException {
if (key == null) {
throw new IllegalArgumentException(“key parameter cannot be null”);
} else if (mdcAdapter == null) {
throw new IllegalStateException(“MDCAdapter cannot be null. See also http://www.slf4j.org/codes.html#null_MDCA”);
} else {
mdcAdapter.put(key, val);
}
}

public static MDC.MDCCloseable putCloseable(String key, String val) throws IllegalArgumentException {
    put(key, val);
    return new MDC.MDCCloseable(key);
}

public static String get(String key) throws IllegalArgumentException {
    if (key == null) {
        throw new IllegalArgumentException("key parameter cannot be null");
    } else if (mdcAdapter == null) {
        throw new IllegalStateException("MDCAdapter cannot be null. See also http://www.slf4j.org/codes.html#null_MDCA");
    } else {
        return mdcAdapter.get(key);
    }
}

public static void remove(String key) throws IllegalArgumentException {
    if (key == null) {
        throw new IllegalArgumentException("key parameter cannot be null");
    } else if (mdcAdapter == null) {
        throw new IllegalStateException("MDCAdapter cannot be null. See also http://www.slf4j.org/codes.html#null_MDCA");
    } else {
        mdcAdapter.remove(key);
    }
}

public static void clear() {
    if (mdcAdapter == null) {
        throw new IllegalStateException("MDCAdapter cannot be null. See also http://www.slf4j.org/codes.html#null_MDCA");
    } else {
        mdcAdapter.clear();
    }
}

二、写过滤器继承GenericFilterBean,请求就能拦截到

@Component
public class CustomerHttpSpanInterceptor extends GenericFilterBean {

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
	try {
		// 填充数据(适用logback、log4j 1.x)
		MDC.put("userId","121242");
		servletRequest.getAttribute("");
		// 填充数据(适用log4j 2.x)
		// ThreadContext.put(Contents.REQUEST_ID, UUID.randomUUID().toString());
		filterChain.doFilter(servletRequest, servletResponse);
	} finally {
		// 请求结束时清除数据,否则会造成内存泄露问题
		MDC.remove("traceId");
	}
}

}
在这里插入图片描述

三、设置日志

在 log4j、logback中配置 %X{userId},即可在日志中打印userId。

[%X{userId:-}] %d{yyyy-MM-dd HH:mm:ss.SSS} [%X{X-B3-TraceId:-}] [%X{X-B3-SpanId:-}] [%thread] %-5level %logger{50} %L - %msg%n
(如果是引用了sleuth,就可以直接使用X-B3-TraceId 和X-B3-SpanId
[%X{userId:-}] %d{yyyy-MM-dd HH:mm:ss.SSS} [%X{X-B3-TraceId:-}] [%X{X-B3-SpanId:-}] [%thread] %-5level %logger{50} %L - %msg%n
)

日志打印实例:
[121242] 2022-07-14 10:52:36.621 [0b34b37d0e7655f0] [0b34b37d0e7655f0] [http-nio-13053-exec-1] DEBUG c.t.dao.IXXXXXDao.queryXXXXInfo 159 - <== Total: 1

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值