You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import java.util.concurrent.TimeUnit;
/**
* @author Ricky Fung
*/
public abstract class TraceContext {
private static final ThreadLocal<Long> timeThreadLocal = new ThreadLocal();
//---------接口耗时统计
/**
* 开始计时
* @return
*/
public static long start() {
long startTime = System.nanoTime();
timeThreadLocal.set(startTime);
return startTime;
}
/**
* 获取接口耗时
* @return
*/
public static long stopAndGet() {
long endTime = System.nanoTime();
Long startTime = timeThreadLocal.get();
if (startTime == null) {
throw new IllegalArgumentException("必须先调用start方法");
}
//移除
timeThreadLocal.remove();
long costTime = endTime - startTime;
return TimeUnit.MILLISECONDS.convert(costTime, TimeUnit.NANOSECONDS);
}
//---------trace相关
public static String getTraceId() {
return MDC.get(CommonConstants.SLF4J_TRACE_ID);
}
public static String getSpanId() {
return MDC.get(CommonConstants.SLF4j_SPAN_ID);
}
/**
* 获取traceId,如果不存在则生成一个
* @return
*/
public static String computeTraceId() {
String traceId = getTraceId();
if (StringUtils.isEmpty(traceId)) {
traceId = UUIDUtils.getId();
}
return traceId;
}
public static void putTraceId(String traceId) {
MDC.put(CommonConstants.SLF4J_TRACE_ID, traceId);
}
public static void putSpanId(String spanId) {
MDC.put(CommonConstants.SLF4j_SPAN_ID, spanId);
}
//-------
public static void removeTraceId() {
MDC.remove(CommonConstants.SLF4J_TRACE_ID);
}
public static void removeSpanId() {
MDC.remove(CommonConstants.SLF4j_SPAN_ID);
}
public static String genSpanId(String spanId) {
if (StringUtils.isEmpty(spanId)) {
return CommonConstants.ROOT_SPAN_ID;
}
return spanId+".1";
}
public static String rootSpanId() {
return CommonConstants.ROOT_SPAN_ID;
}
}
其中,CommonConstants定义了几个常量:
/**
* @author Ricky Fung
*/
public class CommonConstants {
//HTTP请求头字段
public static final String TRACE_ID_HEADER = "X-Trace-Id";
public static final String SPAN_ID_HEADER = "X-Span-Id";
//Logback参数
public static final String SLF4J_TRACE_ID = "X-TraceId";
public static final String SLF4j_SPAN_ID = "X-SpanId";
public static final String ROOT_SPAN_ID = "0";
}
MDC实现日志跟踪
MDC在做日志跟踪的时候用的比较多。一个系统提供服务,提供给其他系统来调用,当其他系统调用的时候,入参带上一个唯一的请求标识(traceId),把这个traceId输出到日志中,这样两个系统直接就会形成一个执行链,用traceId串联起来,当出现错误时,可以在调用方查询对应的请求日志,也可以在服务方查询请求日志。定位问题很方便。输出日志的地方很多,不能每次输出都去获取traceId,拼接到日志中,这样做很不优雅,也很容易遗漏。这个时候使用MDC配合logback中的pattern就很简单啦。
实现思路
首先请求过来,将traceId放到MDC中,然后在pattern中用表达式从MDC中获取到对应的traceId。
对MDC不熟悉的同学可以先阅读一下我的上一篇文章:Slf4j MDC 实现机制与应用
Spring Boot 实战
1、TraceContext
首先,我们封装一个 TraceContext工具类,如下:
其中,CommonConstants定义了几个常量:
2、TraceInterceptor
利用Spring MVC提供的
org.springframework.web.servlet.HandlerInterceptor
我们可以很方便的拦截HTTP请求,对请求处理前后做一些处理。在preHandle方法中,我们获取HttpServletRequest中的TraceId 和 SpanId,如果二者为空我们就生成一个并put到MDC中。
在afterCompletion方法中,我们做一些清理工作,清除我们之前设置的TraceId 和 SpanId。
另外,就是应用在请求三方系统时,需要带上TraceId,这样整个链路就串联起来了,代码如下:
3、日志输出
在logback.xml中如果想输出MDC中的自定义属性,可以通过
%X{propertyName}
方式。logback-spring.xml 如下:
我们通过 %X{X-TraceId} 和 %X{X-SpanId} 输出TraceContext中put到MDC中的TraceId 和 SpanId。
The text was updated successfully, but these errors were encountered: