簡體   English   中英

如何將 Open Telemetry span 傳播到 Spring Boot 中的 Executor threads(或者 span 是否有可能存在於 THREAD_LOCAL 模式下)

[英]How to propagate Open Telemetry span to Executor threads in Spring Boot (or Is it possible for the span to exist in THREAD_LOCAL mode)

我正在嘗試設置一個開放的遙測跟蹤器以傳播到線程,該線程由配置如下的 ThreadPoolTaskExecutor 生成

private DgsDataLoaderAsyncExecutor() {
  ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
  executor.setCorePoolSize(1);
  executor.setMaxPoolSize(100);
  executor.setQueueCapacity(500);
  executor.setThreadNamePrefix("threadAsync");
  executor.setWaitForTasksToCompleteOnShutdown(true);
  executor.setTaskDecorator(new DataLoaderTaskDecorator());
  executor.initialize();
  executorInstance = executor;
  // spring tracer is obtained here to extract the current span later on 
  tracer = GlobalOpenTelemetry.getTracer("org.springframework.boot");
}

后來我這樣稱呼執行者:

/**
 * Only one (or no) Executor will live in the service at all times. It will be retrieved in a thread safe manner
 * @return executor to be used in the DataLoader
 */
public static synchronized Executor get() {
  if (instance == null) {
    instance = new DgsDataLoaderAsyncExecutor();
  }
  return instance.getExecutorInstance();
}

static class DataLoaderTaskDecorator implements TaskDecorator {
  /**
   * Currently, tenant, security, and locale contexts are passed
   */
  @Override
  public Runnable decorate(Runnable runnable) {
    // Current Web Thread context to be passed ot async
    var currentSpanContext = Context.current();
    var currentSpan = Span.current();
    Locale locale = LocaleContextHolder.getLocale();

    log.debug("Saving information for async thread...");
    return () -> {
      // this span shows up in the parent trace as expected 
      Span asyncSpan = tracer.spanBuilder("DGS Executor async op")
        .setParent(currentSpanContext.with(currentSpan))
        .startSpan();
      try {
        // Async thread context
        LocaleContextHolder.setLocale(locale);
        log.debug("Restoring information for async thread...");
        // however the spans in the thread fail to obtain the parent trace and are not linked to it
        runnable.run();
      } catch (Exception e) {
        log.error("Error in async task", e);
      } finally {
        asyncSpan.end();
        log.debug("DgsDataLoader has finished async execution");
      }
    };
  }
}

但是,跨度無法傳播到線程,並且會為在該線程中創建的跨度創建一個新的父級。

我已經能夠通過創建一個自定義上下文來解決這個問題,我在其中打包當前跨度

Span asyncSpan = tracer.spanBuilder("DGS Executor async op")
  .setParent(currentSpanContext.with(currentSpan))
  .startSpan();
try {
  SpanThreadContextHolder.setTracingSpan(asyncSpan);
  // Async thread context
  LocaleContextHolder.setLocale(locale);
  log.debug("Restoring information for async thread...");
  runnable.run();
} catch (Exception e) {
  log.error("Error in async task", e);
} finally {
  SpanThreadContextHolder.removeTracingSpan();

但這會帶來副作用,下游代碼必須意識到它並通過

// inside runnable.run()
var span = SpanThreadContextHolder.getOpenTracingSpan()
if (span!= null) {
    Context.current().with(span).makeCurrent()
}

完成所有這些故事后,我的問題是:是否有可能將跨度自動傳播到從主線程生成的其他線程,讓跨度存在於 THREAD_LOCAL 模式中?

我終於在這里找到了答案。

執行者必須用 otel 上下文包裝:

public ExecutorService wrapExecutor(ExecutorService executor) {
  return Context.taskWrapping(executor);
} 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM