背景
在项目中, 通过一个 orderId 字段来 贯穿 订单的一个执行过程。 通过 这个 orderId 可以解决 90%的问题排查效率问题,也不需要去 去定义 在 分布式系统中的一个 业务 id。 在刚开始时,业务简单,都是在 log.info 中 人工去写:
存在两个问题:
1、随着代码量越来越多,需要不断重复的在 日志中 写上 orderid,心累,同时 作为程序员,应该很 清楚 DRY 原则,
2、不同的人书写的 格式可能 大不相同,,且容易 遗漏。
3、项目会 调用 一个sdk 的三方 应用,目前是没有进行链路绑定的,这个目前还没有特别的好的方案, 但对 sdk的调用 用AOP做了 包装,输出 request和response, 可以 通过 MDC 同 orderId 绑定,方便 异常排查
springboot + MDC 实现 链路追踪
1、项目中实践
logback配置文件修改
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 定义路径 -->
<property name="LOG_PATH" value="logs"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%d] [%level] [orderId:%X{orderId}] [uuid:%X{uuid}] [%X{xRequestId}] %logger - %m%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>
写了工具类
public class MdcUtil {
public static final String ORDER_ID = "orderId";
public static final String UUID = "uuid";
public static void setOrderId(String value) {
put(ORDER_ID, value);
}
public static void setUUid(String value) {
put(UUID, value);
}
public static String getOrderId() {
return MDC.get(ORDER_ID);
}
public static void put(String key, Object value) {
if (key != null) {
MDC.put(key, value.toString());
}
}
public static void clear() {
MDC.clear();
}
}
在 代码中 目前没有统一用filter 或者Intercepter 进行处理,在请求中,目前 并没有统一,所以现在的方案是 针对每个 请求入口进行 侵入式操作。
MdcUtil.setOrderId(orderId);
MdcUtil.clear();
这样对 sdk 进行aop处理的过程中,就能 绑定 orderId 日志输出了。
2、注意点
1、实现的原理 其实比较简单,通过与 Thread 绑定, 所以在进行 线程切换后,需要 处理。
并且 不要 忘了进行 clear,否则在大量请求的情况下 容易造成 ThreadLocal 的 内存溢出。
2、目前只是对关键的执行过程进行了处理。 后面可以对所有的请求 通过 一个 全局的tranceId进行 绑定,贯穿请求的生命周期。
3、原理
见参考文档。
4、思考
1、目前的编码方案,仅仅解决了 一些 代码的重复书写, 对 三方 调用进行aop 操作与 traceId 线程维度的关联
2、针对没有 业务标示的 请求, 目前没有操作。 需要 在请求入口进行 traceId的生成,或者前端请求传入,做成更通用的模版。 可以参考 sleuth,SkyWalking 这些开源的框架