MDC 实现全局 traceId(完整版)

MDC 是什么

MDC 是 Java 中的一种日志记录工具,底层实现是对 ThreadLocal 的封装,以实现线程级别的调用链路追踪,kv 形式存储

生成 TraceId

生成规则:服务器 IP + ID 产生的时间 + 自增序列 + 当前进程号

import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Enumeration;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.lang3.StringUtils;

public class TraceIdGenerator {

  private static String IP_16 = "ffffffff";
  private static AtomicInteger count = new AtomicInteger(1000);

  public static String P_ID_CACHE = null;

  static {
    try {
      String ipAddress = getInetAddress();
      if (ipAddress != null) {
        IP_16 = getIP_16(ipAddress);
      }
    } catch (Throwable e) {
      // ignore
    }
  }

  private static String getTraceId(String ip, long timestamp, int nextId) {
    StringBuilder appender = new StringBuilder(30);
    appender.append(ip).append(timestamp).append(nextId).append(getPID());
    return appender.toString();
  }

  public static String generate() {
    return getTraceId(IP_16, System.currentTimeMillis(), getNextId());
  }

  private static String getIP_16(String ip) {
    String[] ips = ip.split("\\.");
    StringBuilder sb = new StringBuilder();
    for (String column : ips) {
      String hex = Integer.toHexString(Integer.parseInt(column));
      if (hex.length() == 1) {
        sb.append('0').append(hex);
      } else {
        sb.append(hex);
      }
    }
    return sb.toString();
  }

  private static int getNextId() {
    for (; ; ) {
      int current = count.get();
      int next = (current > 9000) ? 1000 : current + 1;
      if (count.compareAndSet(current, next)) {
        return next;
      }
    }
  }

  public static String getInetAddress() {
    try {
      Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
      InetAddress address = null;
      while (interfaces.hasMoreElements()) {
        NetworkInterface ni = interfaces.nextElement();
        Enumeration<InetAddress> addresses = ni.getInetAddresses();
        while (addresses.hasMoreElements()) {
          address = addresses.nextElement();
          if (!address.isLoopbackAddress() && address.getHostAddress().indexOf(":") == -1) {
            return address.getHostAddress();
          }
        }
      }
      return null;
    } catch (Throwable t) {
      return null;
    }
  }

  @SuppressWarnings("AbbreviationAsWordInName")
  public static String getPID() {
    // Check pid is cached.
    if (P_ID_CACHE != null) {
      return P_ID_CACHE;
    }
    String processName = java.lang.management.ManagementFactory.getRuntimeMXBean().getName();

    if (StringUtils.isBlank(processName)) {
      return StringUtils.EMPTY;
    }

    String[] processSplitName = processName.split("@");

    if (processSplitName.length == 0) {
      return StringUtils.EMPTY;
    }

    String pid = processSplitName[0];

    if (StringUtils.isBlank(pid)) {
      return StringUtils.EMPTY;
    }
    P_ID_CACHE = pid;
    return pid;
  }
}

MDC 封装

import com.google.common.collect.Sets;
import java.util.Optional;
import java.util.Set;
import org.slf4j.MDC;

@SuppressWarnings("AbbreviationAsWordInName")
public class MDCUtil {
  String TRACE_ID = "TRACE_ID";
  private static final Set<String> MDC_KEYs = Sets.newHashSet(TRACE_ID);

  private MDCUtil() {}

  public static void putTraceId() {
    putTraceId(TraceIdGenerator.generate());
  }

  public static void putTraceId(String traceId) {
    if (MDC.get(TRACE_ID) == null) {
      MDC.put(TRACE_ID, traceId);
    }
  }

  public static Optional<String> getTraceId() {
    String traceId = MDC.get(TRACE_ID);
    return traceId == null ? Optional.empty() : Optional.of(traceId);
  }

  public static void clear() {
    MDC_KEYs.forEach(key -> MDC.remove(TRACE_ID));
  }
}

traceId 全局拦截器

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.HandlerInterceptor;

@RequiredArgsConstructor
@Service
@Slf4j
public class TraceIdInterceptor implements HandlerInterceptor {

  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {
    MDCUtil.putTraceId();
    return HandlerInterceptor.super.preHandle(request, response, handler);
  }

  @Override
  public void afterCompletion(
      HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
      throws Exception {
    MDCUtil.clear();
    HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
  }
}

拦截器配置

import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@RequiredArgsConstructor
public class TraceIdConfig implements WebMvcConfigurer {

  private final TraceIdInterceptor traceIdInterceptor;

  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(traceIdInterceptor).addPathPatterns("/**");
  }
}

猜你喜欢

转载自blog.csdn.net/qq_38685503/article/details/130325172