[英]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.