logback 的mdc官方文档 see: 链接

MDC 简介

在一个分布式系统, 存在很多应用,一个调用链路会很长。为了唯一标记每个请求,我们可以将线程名称,用户id 等一些上下文信息放入 MDC。我们可以通过 grep 从大量日志中找到我们需要定位问题的日志。

slf4j MDC 提供的方法:

  1. package org.slf4j;
  2. public class MDC {
  3. //Put a context value as identified by key
  4. //into the current thread's context map.
  5. public static void put(String key, String val);
  6. //Get the context identified by the key parameter.
  7. public static String get(String key);
  8. //Remove the context identified by the key parameter.
  9. public static void remove(String key);
  10. //Clear all entries in the MDC.
  11. public static void clear();
  12. }

slf4j 不做任何实现,具体实现通过mdcAdapter 调用具体 logback 等日志实现。
分布式系统的请求链路:

image.png

Spring Cloud Sleuth 使用

在基于 springCloud 的微服务架构下,分布式链路追踪,使用 Spring Cloud Sleuth。Spring Cloud Sleuth 就使用了 MDC 设置服务的 traceId。官网介绍:

Spring Cloud Sleuth provides Spring Boot auto-configuration for distributed tracing.

Features

Sleuth configures everything you need to get started. This includes where trace data (spans) are reported to, how many traces to keep (sampling), if remote fields (baggage) are sent, and which libraries are traced. Specifically, Spring Cloud Sleuth…

  • Adds trace and span ids to the Slf4J MDC, so you can extract all the logs from a given trace or span in a log aggregator.
  • Instruments common ingress and egress points from Spring applications (servlet filter, rest template, scheduled actions, message channels, feign client).
  • If spring-cloud-sleuth-zipkin is available then the app will generate and report Zipkin-compatible traces via HTTP. By default it sends them to a Zipkin collector service on localhost (port 9411). Configure the location of the service using spring.zipkin.baseUrl.

在程序中,可以直接给MDC 中设置值,MDC.put(“corpId”, 123),在logback.xml 中通过 %X 配置占位符

  1. <springProperty name="logging.pattern" source="logging.pattern"
  2. defaultValue="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{X-B3-TraceId:-},%X{X-B3-ParentSpanId:-},%X{X-B3-SpanId:-},%X{userId},%X{corpId}] %-5level %logger{10} - %msg%n"/>

工作原理

MDC值的设置

org.springframework.cloud.sleuth.log.Slf4jCurrentTraceContext

  1. package org.springframework.cloud.sleuth.log;
  2. import brave.internal.HexCodec;
  3. import brave.internal.Nullable;
  4. import brave.propagation.CurrentTraceContext;
  5. import brave.propagation.TraceContext;
  6. import org.slf4j.Logger;
  7. import org.slf4j.LoggerFactory;
  8. import org.slf4j.MDC;
  9. /**
  10. * Adds {@linkplain org.slf4j.MDC} properties "traceId", "parentId", "spanId" and "spanExportable" when a {@link
  11. * brave.Tracer#currentSpan() span is current}. These can be used in log correlation.
  12. * Supports backward compatibility of MDC entries by adding legacy "X-B3" entries to MDC context
  13. * "X-B3-TraceId", "X-B3-ParentSpanId", "X-B3-SpanId" and "X-B3-Sampled"
  14. *
  15. * @author Marcin Grzejszczak
  16. *
  17. * @since 2.0.0
  18. */
  19. public final class Slf4jCurrentTraceContext extends CurrentTraceContext {
  20. // Backward compatibility for all logging patterns
  21. private static final String LEGACY_EXPORTABLE_NAME = "X-Span-Export";
  22. private static final String LEGACY_PARENT_ID_NAME = "X-B3-ParentSpanId";
  23. private static final String LEGACY_TRACE_ID_NAME = "X-B3-TraceId";
  24. private static final String LEGACY_SPAN_ID_NAME = "X-B3-SpanId";
  25. private static final Logger log = LoggerFactory
  26. .getLogger(Slf4jCurrentTraceContext.class);
  27. public static Slf4jCurrentTraceContext create() {
  28. return create(CurrentTraceContext.Default.inheritable());
  29. }
  30. public static Slf4jCurrentTraceContext create(CurrentTraceContext delegate) {
  31. return new Slf4jCurrentTraceContext(delegate);
  32. }
  33. final CurrentTraceContext delegate;
  34. Slf4jCurrentTraceContext(CurrentTraceContext delegate) {
  35. if (delegate == null)
  36. throw new NullPointerException("delegate == null");
  37. this.delegate = delegate;
  38. }
  39. @Override public TraceContext get() {
  40. return this.delegate.get();
  41. }
  42. @Override public Scope newScope(@Nullable TraceContext currentSpan) {
  43. final String previousTraceId = MDC.get("traceId");
  44. final String previousParentId = MDC.get("parentId");
  45. final String previousSpanId = MDC.get("spanId");
  46. final String spanExportable = MDC.get("spanExportable");
  47. final String legacyPreviousTraceId = MDC.get(LEGACY_TRACE_ID_NAME);
  48. final String legacyPreviousParentId = MDC.get(LEGACY_PARENT_ID_NAME);
  49. final String legacyPreviousSpanId = MDC.get(LEGACY_SPAN_ID_NAME);
  50. final String legacySpanExportable = MDC.get(LEGACY_EXPORTABLE_NAME);
  51. if (currentSpan != null) {
  52. String traceIdString = currentSpan.traceIdString();
  53. MDC.put("traceId", traceIdString);
  54. MDC.put(LEGACY_TRACE_ID_NAME, traceIdString);
  55. String parentId = currentSpan.parentId() != null ?
  56. HexCodec.toLowerHex(currentSpan.parentId()) :
  57. null;
  58. replace("parentId", parentId);
  59. replace(LEGACY_PARENT_ID_NAME, parentId);
  60. String spanId = HexCodec.toLowerHex(currentSpan.spanId());
  61. MDC.put("spanId", spanId);
  62. MDC.put(LEGACY_SPAN_ID_NAME, spanId);
  63. String sampled = String.valueOf(currentSpan.sampled());
  64. MDC.put("spanExportable", sampled);
  65. MDC.put(LEGACY_EXPORTABLE_NAME, sampled);
  66. log("Starting scope for span: {}", currentSpan);
  67. if (currentSpan.parentId() != null) {
  68. if (log.isTraceEnabled()) {
  69. log.trace("With parent: {}", currentSpan.parentId());
  70. }
  71. }
  72. }
  73. else {
  74. MDC.remove("traceId");
  75. MDC.remove("parentId");
  76. MDC.remove("spanId");
  77. MDC.remove("spanExportable");
  78. MDC.remove(LEGACY_TRACE_ID_NAME);
  79. MDC.remove(LEGACY_PARENT_ID_NAME);
  80. MDC.remove(LEGACY_SPAN_ID_NAME);
  81. MDC.remove(LEGACY_EXPORTABLE_NAME);
  82. }
  83. Scope scope = this.delegate.newScope(currentSpan);
  84. class ThreadContextCurrentTraceContextScope implements Scope {
  85. @Override public void close() {
  86. log("Closing scope for span: {}", currentSpan);
  87. scope.close();
  88. replace("traceId", previousTraceId);
  89. replace("parentId", previousParentId);
  90. replace("spanId", previousSpanId);
  91. replace("spanExportable", spanExportable);
  92. replace(LEGACY_TRACE_ID_NAME, legacyPreviousTraceId);
  93. replace(LEGACY_PARENT_ID_NAME, legacyPreviousParentId);
  94. replace(LEGACY_SPAN_ID_NAME, legacyPreviousSpanId);
  95. replace(LEGACY_EXPORTABLE_NAME, legacySpanExportable);
  96. }
  97. }
  98. return new ThreadContextCurrentTraceContextScope();
  99. }
  100. private void log(String text, TraceContext span) {
  101. if (span == null) {
  102. return;
  103. }
  104. if (log.isTraceEnabled()) {
  105. log.trace(text, span);
  106. }
  107. }
  108. static void replace(String key, @Nullable String value) {
  109. if (value != null) {
  110. MDC.put(key, value);
  111. }
  112. else {
  113. MDC.remove(key);
  114. }
  115. }
  116. }

上面类,会将请求 span 里面的 traceId,spanId,X-B3-TraceId”, “X-B3-ParentSpanId”, “X-B3-SpanId” and “X-B3-Sampled” 设置到 MDC 上下文。

Rest 请求处理

TracingFilter 实现了 servlet 的 filter 接口,在filter 方法中进行了处理

  1. package brave.servlet;
  2. import brave.Span;
  3. import brave.SpanCustomizer;
  4. import brave.Tracer;
  5. import brave.Tracing;
  6. import brave.http.HttpServerHandler;
  7. import brave.http.HttpTracing;
  8. import brave.propagation.CurrentTraceContext;
  9. import brave.propagation.CurrentTraceContext.Scope;
  10. import brave.propagation.Propagation.Getter;
  11. import brave.propagation.TraceContext;
  12. import java.io.IOException;
  13. import javax.servlet.Filter;
  14. import javax.servlet.FilterChain;
  15. import javax.servlet.FilterConfig;
  16. import javax.servlet.ServletException;
  17. import javax.servlet.ServletRequest;
  18. import javax.servlet.ServletResponse;
  19. import javax.servlet.http.HttpServletRequest;
  20. import javax.servlet.http.HttpServletResponse;
  21. public final class TracingFilter implements Filter {
  22. static final Getter<HttpServletRequest, String> GETTER =
  23. new Getter<HttpServletRequest, String>() {
  24. @Override public String get(HttpServletRequest carrier, String key) {
  25. return carrier.getHeader(key);
  26. }
  27. @Override public String toString() {
  28. return "HttpServletRequest::getHeader";
  29. }
  30. };
  31. static final HttpServletAdapter ADAPTER = new HttpServletAdapter();
  32. public static Filter create(Tracing tracing) {
  33. return new TracingFilter(HttpTracing.create(tracing));
  34. }
  35. public static Filter create(HttpTracing httpTracing) {
  36. return new TracingFilter(httpTracing);
  37. }
  38. final ServletRuntime servlet = ServletRuntime.get();
  39. final CurrentTraceContext currentTraceContext;
  40. final Tracer tracer;
  41. final HttpServerHandler<HttpServletRequest, HttpServletResponse> handler;
  42. final TraceContext.Extractor<HttpServletRequest> extractor;
  43. TracingFilter(HttpTracing httpTracing) {
  44. tracer = httpTracing.tracing().tracer();
  45. currentTraceContext = httpTracing.tracing().currentTraceContext();
  46. handler = HttpServerHandler.create(httpTracing, ADAPTER);
  47. extractor = httpTracing.tracing().propagation().extractor(GETTER);
  48. }
  49. @Override
  50. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
  51. throws IOException, ServletException {
  52. HttpServletRequest httpRequest = (HttpServletRequest) request;
  53. HttpServletResponse httpResponse = servlet.httpResponse(response);
  54. //1.防止同一个请求存在相同的 span
  55. // Prevent duplicate spans for the same request
  56. TraceContext context = (TraceContext) request.getAttribute(TraceContext.class.getName());
  57. if (context != null) {
  58. // A forwarded request might end up on another thread, so make sure it is scoped
  59. Scope scope = currentTraceContext.maybeScope(context);
  60. try {
  61. chain.doFilter(request, response);
  62. } finally {
  63. scope.close();
  64. }
  65. return;
  66. }
  67. Span span = handler.handleReceive(extractor, httpRequest);
  68. // Add attributes for explicit access to customization or span context
  69. request.setAttribute(SpanCustomizer.class.getName(), span.customizer());
  70. request.setAttribute(TraceContext.class.getName(), span.context());
  71. Throwable error = null;
  72. // 调用 Slf4jCurrentTraceContext 维护span
  73. Scope scope = currentTraceContext.newScope(span.context());
  74. try {
  75. // any downstream code can see Tracer.currentSpan() or use Tracer.currentSpanCustomizer()
  76. chain.doFilter(httpRequest, httpResponse);
  77. } catch (IOException | ServletException | RuntimeException | Error e) {
  78. error = e;
  79. throw e;
  80. } finally {
  81. scope.close();
  82. if (servlet.isAsync(httpRequest)) { // we don't have the actual response, handle later
  83. servlet.handleAsync(handler, httpRequest, httpResponse, span);
  84. } else { // we have a synchronous response, so we can finish the span
  85. handler.handleSend(ADAPTER.adaptResponse(httpRequest, httpResponse), error, span);
  86. }
  87. }
  88. }
  89. @Override public void destroy() {
  90. }
  91. @Override
  92. public void init(FilterConfig filterConfig) {
  93. }
  94. }

Async 请求

通过 aop 切面实现,如果有需求对异步的任务进行增强,可以学习参考这块的代码,不需要重复造轮子。。

  1. @Aspect
  2. public class TraceAsyncAspect {
  3. private static final String CLASS_KEY = "class";
  4. private static final String METHOD_KEY = "method";
  5. private final Tracer tracer;
  6. private final SpanNamer spanNamer;
  7. public TraceAsyncAspect(Tracer tracer, SpanNamer spanNamer) {
  8. this.tracer = tracer;
  9. this.spanNamer = spanNamer;
  10. }
  11. @Around("execution (@org.springframework.scheduling.annotation.Async * *.*(..))")
  12. public Object traceBackgroundThread(final ProceedingJoinPoint pjp) throws Throwable {
  13. String spanName = name(pjp);
  14. Span span = this.tracer.currentSpan();
  15. if (span == null) {
  16. span = this.tracer.nextSpan();
  17. }
  18. span = span.name(spanName);
  19. try(Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
  20. span.tag(CLASS_KEY, pjp.getTarget().getClass().getSimpleName());
  21. span.tag(METHOD_KEY, pjp.getSignature().getName());
  22. return pjp.proceed();
  23. } finally {
  24. span.finish();
  25. }
  26. }
  27. String name(ProceedingJoinPoint pjp) {
  28. return this.spanNamer.name(getMethod(pjp, pjp.getTarget()),
  29. SpanNameUtil.toLowerHyphen(pjp.getSignature().getName()));
  30. }
  31. private Method getMethod(ProceedingJoinPoint pjp, Object object) {
  32. MethodSignature signature = (MethodSignature) pjp.getSignature();
  33. Method method = signature.getMethod();
  34. return ReflectionUtils
  35. .findMethod(object.getClass(), method.getName(), method.getParameterTypes());
  36. }

这里对 runnable 和Callable 进行了装饰增强,在具体任务执行前,进行了请求span 的设置

  1. public class TraceCallable<V> implements Callable<V> {
  2. /**
  3. * Since we don't know the exact operation name we provide a default
  4. * name for the Span
  5. */
  6. private static final String DEFAULT_SPAN_NAME = "async";
  7. private final Tracer tracer;
  8. private final Callable<V> delegate;
  9. private final TraceContext parent;
  10. private final String spanName;
  11. public TraceCallable(Tracing tracing, SpanNamer spanNamer, Callable<V> delegate) {
  12. this(tracing, spanNamer, delegate, null);
  13. }
  14. public TraceCallable(Tracing tracing, SpanNamer spanNamer, Callable<V> delegate, String name) {
  15. this.tracer = tracing.tracer();
  16. this.delegate = delegate;
  17. this.parent = tracing.currentTraceContext().get();
  18. this.spanName = name != null ? name : spanNamer.name(delegate, DEFAULT_SPAN_NAME);
  19. }
  20. @Override public V call() throws Exception {
  21. ScopedSpan span = this.tracer.startScopedSpanWithParent(this.spanName, this.parent);
  22. try {
  23. return this.delegate.call();
  24. } catch (Exception | Error e) {
  25. span.error(e);
  26. throw e;
  27. } finally {
  28. span.finish();
  29. }
  30. }
  31. }
  1. public class TraceRunnable implements Runnable {
  2. /**
  3. * Since we don't know the exact operation name we provide a default
  4. * name for the Span
  5. */
  6. private static final String DEFAULT_SPAN_NAME = "async";
  7. private final Tracer tracer;
  8. private final Runnable delegate;
  9. private final TraceContext parent;
  10. private final String spanName;
  11. public TraceRunnable(Tracing tracing, SpanNamer spanNamer, Runnable delegate) {
  12. this(tracing, spanNamer, delegate, null);
  13. }
  14. public TraceRunnable(Tracing tracing, SpanNamer spanNamer, Runnable delegate, String name) {
  15. this.tracer = tracing.tracer();
  16. this.delegate = delegate;
  17. this.parent = tracing.currentTraceContext().get();
  18. this.spanName = name != null ? name : spanNamer.name(delegate, DEFAULT_SPAN_NAME);
  19. }
  20. @Override
  21. public void run() {
  22. ScopedSpan span = this.tracer.startScopedSpanWithParent(this.spanName, this.parent);
  23. try {
  24. this.delegate.run();
  25. } catch (Exception | Error e) {
  26. span.error(e);
  27. throw e;
  28. } finally {
  29. span.finish();
  30. }
  31. }
  32. }

ScheduledExecutorService 和 ExecutorService 也使用了装饰器模式进行增强,任务执行时,传入了增强后的 runnable 和callable 对象。这里可以借鉴,如果需要对线程池执行的任务进行增强,比如在任务的执行前后做一些统计,做一个额外的操作,可以借鉴这种思路。

  1. public class TraceableScheduledExecutorService extends TraceableExecutorService implements ScheduledExecutorService {
  2. public TraceableScheduledExecutorService(BeanFactory beanFactory, final ExecutorService delegate) {
  3. super(beanFactory, delegate);
  4. }
  5. private ScheduledExecutorService getScheduledExecutorService() {
  6. return (ScheduledExecutorService) this.delegate;
  7. }
  8. @Override
  9. public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
  10. Runnable r = new TraceRunnable(tracing(), spanNamer(), command);
  11. return getScheduledExecutorService().schedule(r, delay, unit);
  12. }
  13. @Override
  14. public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
  15. Callable<V> c = new TraceCallable<>(tracing(), spanNamer(), callable);
  16. return getScheduledExecutorService().schedule(c, delay, unit);
  17. }
  18. @Override
  19. public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
  20. Runnable r = new TraceRunnable(tracing(), spanNamer(), command);
  21. return getScheduledExecutorService().scheduleAtFixedRate(r, initialDelay, period, unit);
  22. }
  23. @Override
  24. public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) {
  25. Runnable r = new TraceRunnable(tracing(), spanNamer(), command);
  26. return getScheduledExecutorService().scheduleWithFixedDelay(r, initialDelay, delay, unit);
  27. }
  28. }

Scheduling 任务

周期任务上下文也是通过切面增强实现

  1. @Aspect
  2. public class TraceSchedulingAspect {
  3. private static final String CLASS_KEY = "class";
  4. private static final String METHOD_KEY = "method";
  5. private final Tracer tracer;
  6. private final Pattern skipPattern;
  7. public TraceSchedulingAspect(Tracer tracer, Pattern skipPattern) {
  8. this.tracer = tracer;
  9. this.skipPattern = skipPattern;
  10. }
  11. @Around("execution (@org.springframework.scheduling.annotation.Scheduled * *.*(..))")
  12. public Object traceBackgroundThread(final ProceedingJoinPoint pjp) throws Throwable {
  13. if (this.skipPattern.matcher(pjp.getTarget().getClass().getName()).matches()) {
  14. return pjp.proceed();
  15. }
  16. String spanName = SpanNameUtil.toLowerHyphen(pjp.getSignature().getName());
  17. Span span = startOrContinueRenamedSpan(spanName);
  18. try(Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) {
  19. span.tag(CLASS_KEY, pjp.getTarget().getClass().getSimpleName());
  20. span.tag(METHOD_KEY, pjp.getSignature().getName());
  21. return pjp.proceed();
  22. } finally {
  23. span.finish();
  24. }
  25. }
  26. private Span startOrContinueRenamedSpan(String spanName) {
  27. Span currentSpan = this.tracer.currentSpan();
  28. if (currentSpan != null) {
  29. return currentSpan.name(spanName);
  30. }
  31. return this.tracer.nextSpan().name(spanName);
  32. }
  33. }

使用

日志追溯

为了方便排查问题,每个请求的response ,都返回了一个 traceId, traceId 是一个完整链路的唯一标识。通过traceId,去查询日志,直接可以获取当前请求的全链路日志信息。
image.png

在服务器端查询日志,通过traceId可以把整个请求链路串起来

  1. ## 网关日志
  2. [commuser1@lg-h-1-94 log]$ grep 24ee988678463fb3 log.log
  3. 2021-11-24 09:47:48.593 [reactor-http-epoll-14] [24ee988678463fb3,,24ee988678463fb3,,] INFO c.e.p.m.g.s.WebsiteGatewayService - request token:a9d72f3a-9ed6-4b80-8fbf-dbb0b0e91d93
  4. ## busi服务日志
  5. [commuser1@lg-h-1-94 log]$ grep 24ee988678463fb3 log.log
  6. 2021-11-24 09:47:48.602 [http-nio-30030-exec-1] [24ee988678463fb3,24ee988678463fb3,aee8b388cf00d0cf,314117,193729,] INFO c.e.p.t.b.s.i.WaybillServiceImpl - @pageQueryByCondition waybillQueryVO:{
  7. 2021-11-24 09:47:48.611 [http-nio-30030-exec-1] [24ee988678463fb3,24ee988678463fb3,aee8b388cf00d0cf,314117,193729,] INFO c.e.p.l.d.f.s.i.DefaultLevelDataFiltrationRuleKeyServiceImpl - DefaultLevelDataFiltrationRuleKeyServiceImpl.getCurrentUserDataFiltrationRuleKey()方法获取的用户信息=【[{"userType":0,"corpId":193729,"orgId":-85485}]】
  8. 2021-11-24 09:47:48.774 [http-nio-30030-exec-1] [24ee988678463fb3,24ee988678463fb3,aee8b388cf00d0cf,314117,193729,] INFO c.e.p.l.d.f.s.i.DefaultLevelDataFiltrationRuleKeyServiceImpl - DefaultLevelDataFiltrationRuleKeyServiceImpl.getCurrentUserDataFiltrationRuleKey()方法获取的用户信息=【[{"userType":0,"corpId":193729,"orgId":-85485}]】
  9. 2021-11-24 09:47:48.932 [http-nio-30030-exec-1] [24ee988678463fb3,24ee988678463fb3,aee8b388cf00d0cf,314117,193729,] INFO c.e.p.t.f.s.u.ExtendFieldHandleUtil - 获取公司193729表对应扩展字段设置:waybill_info
  10. 2021-11-24 09:47:48.933 [http-nio-30030-exec-1] [24ee988678463fb3,24ee988678463fb3,aee8b388cf00d0cf,314117,193729,] INFO c.e.p.t.f.s.u.ExtendFieldHandleUtil - 获取公司对应扩展字段设置结果:{"extendName":[{"extendFieldName":"ext_str7","extendJson":"{}","extendLabel":"集装箱号","extendType":1,"searchType":1},{"extendFieldName":"ext_str3","extendJson":"{}","extendLabel":"起始日期","extendType":3,"searchType":3},{"extendFieldName":"ext_str2","extendJson":"{}","extendLabel":"账单","extendType":2,"searchType":2},{"extendFieldName":"ext_str1","extendJson":"{}","extendLabel":"QQ号","extendType":1,"searchType":1},{"extendFieldName":"ext_str8","extendJson":"{\"cacheKey\":\"BASE_VEHICLE_MAP\"}","extendLabel":"扩展车辆","extendType":7,"searchType":7},{"extendFieldName":"ext_str10","extendJson":"{\"cacheKey\":\"BASE_VEHICLE_MAP\"}","extendLabel":"超超超超","extendType":7,"searchType":7},{"extendFieldName":"ext_str4","extendJson":"{}","extendLabel":"结���日期","extendType":3,"searchType":4},{"extendFieldName":"ext_str9","extendJson":"{\"cacheKey\":\"BASE_CARRIER_MAP\"}","extendLabel":"大胆的加","extendType":5,"searchType":5},{"extendFieldName":"ext_str6","extendJson":"{\"cacheKey\":\"additionalRequirements\"}","extendLabel":"附加要求","extendType":4,"searchType":5}],"primaryKeyName":"id"}
  11. 2021-11-24 09:47:48.934 [http-nio-30030-exec-1] [24ee988678463fb3,24ee988678463fb3,aee8b388cf00d0cf,314117,193729,] INFO c.e.p.t.b.s.i.WaybillServiceImpl - @totalCount:22,waybillInfoPOList:20
  12. 2021-11-24 09:47:49.407 [http-nio-30030-exec-1] [24ee988678463fb3,24ee988678463fb3,aee8b388cf00d0cf,314117,193729,] INFO c.e.p.t.c.c.s.i.CacheFetchDataRpcServiceImpl - rpc获取缓存:CORP_CAR_MODEL_MAP
  13. 2021-11-24 09:47:49.407 [http-nio-30030-exec-1] [24ee988678463fb3,24ee988678463fb3,aee8b388cf00d0cf,314117,193729,] INFO c.e.p.t.c.c.s.i.CacheFetchDataRpcServiceImpl - rpc获取缓存urlhttp://E6-MS-TMS-BASE-WEB/CORP_CAR_MODEL_MAP?modifyTime
  14. 2021-11-24 09:47:49.514 [http-nio-30030-exec-1] [24ee988678463fb3,24ee988678463fb3,aee8b388cf00d0cf,314117,193729,] INFO c.e.p.t.c.c.s.i.CacheServiceRedisImpl - 更新缓存:CORP_CAR_MODEL_MAP193729,大小:72
  15. 2021-11-24 09:47:49.537 [http-nio-30030-exec-1] [24ee988678463fb3,24ee988678463fb3,aee8b388cf00d0cf,314117,193729,] INFO c.e.p.t.b.a.i.w.u.a.WaybillUploadReceiptAble - 运单:622678780454305792,转运状态:false
  16. 2021-11-24 09:47:49.543 [http-nio-30030-exec-1] [24ee988678463fb3,24ee988678463fb3,aee8b388cf00d0cf,314117,193729,] INFO c.e.p.t.b.a.i.w.u.a.WaybillUploadReceiptAble - 运单:622662423156023296,转运���态:false
  17. 2021-11-24 09:47:49.548 [http-nio-30030-exec-1] [24ee988678463fb3,24ee988678463fb3,aee8b388cf00d0cf,314117,193729,] INFO c.e.p.t.b.a.i.w.u.a.WaybillUploadReceiptAble - 运单:622604542533230592,转运状态:false
  18. 2021-11-24 09:47:49.550 [http-nio-30030-exec-1] [24ee988678463fb3,24ee988678463fb3,aee8b388cf00d0cf,314117,193729,] INFO c.e.p.t.b.a.i.w.s.a.WaybillShipmentConfirmAble - 运单622604542533230592确认装货操作不允许
  19. 2021-11-24 09:47:49.550 [http-nio-30030-exec-1] [24ee988678463fb3,24ee988678463fb3,aee8b388cf00d0cf,314117,193729,] INFO c.e.p.t.b.a.i.w.a.a.WaybillArriveAble - 运单:622604542533230592,不允许到达
  20. 2021-11-24 09:47:49.550 [http-nio-30030-exec-1] [24ee988678463fb3,24ee988678463fb3,aee8b388cf00d0cf,314117,193729,] INFO c.e.p.t.b.a.i.w.l.a.WaybillLeaveAble - Not Found Order, waybill: 622604542533230592
  21. 2021-11-24 09:47:49.551 [http-nio-30030-exec-1] [24ee988678463fb3,24ee988678463fb3,aee8b388cf00d0cf,314117,193729,] INFO c.e.p.t.b.a.i.w.t.a.WaybillTransferAble - Not Found loadNodeList
  22. 2021-11-24 09:47:49.552 [http-nio-30030-exec-1] [24ee988678463fb3,24ee988678463fb3,aee8b388cf00d0cf,314117,193729,] INFO c.e.p.t.c.c.e.E6TransformControllerAdviceProcessor - idname耗时:1
  23. 2021-11-24 09:47:49.565 [http-nio-30030-exec-1] [24ee988678463fb3,24ee988678463fb3,aee8b388cf00d0cf,314117,193729,] INFO c.e.p.c.w.E6CommonFilter - /waybill/pageQueryByCondition, 请求耗时: 970 ms
  24. ## base 服务日志(busi rpc调用)
  25. [commuser1@2lg-1-25 log]$ grep 24ee988678463fb3 log.log
  26. 2021-11-24 09:47:49.515 [http-nio-30020-exec-4] [24ee988678463fb3,aee8b388cf00d0cf,9d3971a63dc5950e,,,] DEBUG c.e.p.t.b.b.m.C.corpCarModelMap - ==> Preparing: SELECT id, `name` label FROM car_model_info WHERE corp_id = ? AND valid = 1
  27. 2021-11-24 09:47:49.515 [http-nio-30020-exec-4] [24ee988678463fb3,aee8b388cf00d0cf,9d3971a63dc5950e,,,] DEBUG c.e.p.t.b.b.m.C.corpCarModelMap - ==> Parameters: 193729(Integer)
  28. 2021-11-24 09:47:49.518 [http-nio-30020-exec-4] [24ee988678463fb3,aee8b388cf00d0cf,9d3971a63dc5950e,,,] DEBUG c.e.p.t.b.b.m.C.corpCarModelMap - <== Total: 72
  29. 2021-11-24 09:47:49.519 [http-nio-30020-exec-4] [24ee988678463fb3,aee8b388cf00d0cf,9d3971a63dc5950e,,,] INFO c.e.p.c.w.E6CommonFilter - /CORP_CAR_MODEL_MAP, 请求耗时: 7 ms
  30. ## base 服务日志(busi rpc调用)
  31. [commuser1@lg-h-1-94 log]$ grep 24ee988678463fb3 log.log
  32. 2021-11-24 09:47:49.115 [http-nio-30020-exec-9] [24ee988678463fb3,aee8b388cf00d0cf,a1b9f8881f547fdf,314117,193729,E6-MS-TMS-BUSI-WEB] DEBUG c.e.p.t.b.b.m.B.findByCorpIdAndIds - ==> Preparing: SELECT line.remark, line.id, line.NAME, line.car_id, line.driver_id, line.department_id, line.area_name web_sit, line.total_odo, line.corp_id, line.version, line.created_time, line.modified_time, line.created_user_id, line_area.id AS line_id, line_area.fixed_line_id, line_area.custom_area_id, line_area.arrive_time, line_area.leave_time, line_area.day_span_num, line_area.transport_duration, line_area.stay_duration, line_area.corp_id AS area_corp_id, line_area.version AS area_version, line_area.order_index AS order_index, line_area.node_type AS node_type, line_dept.dept_id FROM base_fixed_line_info line LEFT JOIN base_fixed_line_area line_area ON line.id = line_area.fixed_line_id AND line_area.valid = 1 LEFT JOIN base_fixed_line_dept line_dept ON line.id = line_dept.fixed_line_id AND line_dept.valid = 1 WHERE line.valid = 1 AND line.corp_id = ? AND line.id IN (?, ?, ?, ?, ?, ?, ?, ?)
  33. 2021-11-24 09:47:49.115 [http-nio-30020-exec-9] [24ee988678463fb3,aee8b388cf00d0cf,a1b9f8881f547fdf,314117,193729,E6-MS-TMS-BUSI-WEB] DEBUG c.e.p.t.b.b.m.B.findByCorpIdAndIds - ==> Parameters: 193729(Integer), 575919374336458752(String), 617978466980659200(String), 572201287230488576(String), 590758690929573888(String), 564686140972269568(String), 603073434750484480(String), 518591504745955328(String), 607460392757297152(String)
  34. 2021-11-24 09:47:49.396 [http-nio-30020-exec-9] [24ee988678463fb3,aee8b388cf00d0cf,a1b9f8881f547fdf,314117,193729,E6-MS-TMS-BUSI-WEB] DEBUG c.e.p.t.b.b.m.B.findByCorpIdAndIds - <== Total: 35
  35. 2021-11-24 09:47:49.400 [http-nio-30020-exec-9] [24ee988678463fb3,aee8b388cf00d0cf,a1b9f8881f547fdf,314117,193729,E6-MS-TMS-BUSI-WEB] INFO c.e.p.t.c.c.e.E6TransformControllerAdviceProcessor - idname耗时:3
  36. 2021-11-24 09:47:49.402 [http-nio-30020-exec-9] [24ee988678463fb3,aee8b388cf00d0cf,a1b9f8881f547fdf,314117,193729,E6-MS-TMS-BUSI-WEB] INFO c.e.p.c.w.E6CommonFilter - /rpc/com.e6yun.project.tms.base.rpc.service.TmsBaseFixedLineRpcService/getFixedLineInfoByCorpIdAndLineIds, 请求耗时: 289 ms

实现

在网关,从MDC 中获取 traceId 设置到响应头

  1. @Component
  2. public class DefaultTraceFilter implements GlobalFilter, Ordered {
  3. private static final String TRACE_ID_KEY = "e6api-trace-id";
  4. @Override
  5. public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
  6. headerSetTraceId(exchange);
  7. return chain.filter(exchange);
  8. }
  9. private void headerSetTraceId(ServerWebExchange exchange) {
  10. String traceId = MDC.get("traceId");
  11. if (StringUtils.isNotBlank(traceId)) {
  12. ServerHttpResponse response = exchange.getResponse();
  13. HttpHeaders headers = response.getHeaders();
  14. headers.add(TRACE_ID_KEY, traceId);
  15. exchange.mutate().response(response).build();
  16. }
  17. }
  18. @Override
  19. public int getOrder() {
  20. return -11;
  21. }
  22. }