opentelemetry отсутствует трассировка

Вопрос или проблема

Я пытаюсь использовать автоматическую инструментализацию opentelemetry. Но, похоже, след теряется при использовании CompletableFuture.handle. Не могли бы вы помочь мне с этой проблемой?

@GetMapping("/future3")
public CompletableFuture<ResponseEntity<String>> future3() {
    LOGGER.info("future1 ");  // <- у нас есть след здесь
    LOGGER.info("trace={}", Span.current().getSpanContext().getTraceId()); // <- у нас есть след здесь

    SqsAsyncClient sqsAsyncClient = SqsAsyncClient.builder().build(); 
    CompletableFuture<ResponseEntity<String>> aa = sqsAsyncClient
            .sendMessage(build -> build.queueUrl(queueUrl).messageBody("hello"))
            .handle((response, error) -> {
                LOGGER.info("trace response={}", Span.current().getSpanContext().getTraceId()); // <- мы потеряли след  

                LOGGER.info("test response={}", response);   // <- мы потеряли след  
                return new ResponseEntity<>("success", HttpStatus.OK);

            });
    LOGGER.info("future1_1"); // <- у нас есть идентификатор следа 
    return aa;

}

У меня была такая же проблема, и, к счастью, я могу предоставить вам решение. Из вашего кода я вижу, что проблема, с которой вы столкнулись, связана с потерей контекста следа при использовании CompletableFuture.handle. В качестве решения я предлагаю использовать Context или, возможно, Span, что является хорошим выбором для явной передачи функции OpenTelemetry, чтобы гарантировать, что контекст следа передается в асинхронный обратный вызов.
Во-первых, Context может захватывать и передавать явно асинхронным задачам. Я предлагаю захватить текущий контекст в начале метода и затем использовать этот захваченный контекст внутри вашего метода .handle

Хорошо, я также внес некоторые изменения в код, чтобы помочь вам быстрее получить результат, и вот этот код:

import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;

@GetMapping("/future3")
public CompletableFuture<ResponseEntity<String>> future3() {
LOGGER.info("future1 ");
LOGGER.info("trace={}", Span.current().getSpanContext().getTraceId());

SqsAsyncClient sqsAsyncClient = SqsAsyncClient.builder().build();

// Захват текущего контекста OpenTelemetry

Context currentContext = Context.current();

CompletableFuture<ResponseEntity<String>> aa = sqsAsyncClient
        .sendMessage(build -> build.queueUrl(queueUrl).messageBody("hello"))
        .handle((response, error) -> {

// Сделать контекст OpenTelemetry текущим внутри этого блока handle

            try (Scope scope = currentContext.makeCurrent()) {
                LOGGER.info("trace response={}", Span.current().getSpanContext().getTraceId());
                LOGGER.info("test response={}", response);
                return new ResponseEntity<>("success", HttpStatus.OK);
            }
        });

LOGGER.info("future1_1");
return aa;
}

Это все, чем я могу помочь, надеюсь, это сработает. Удачи 😉🙏

Ответ или решение

Решение проблемы потери трассировки в OpenTelemetry при использовании CompletableFuture.handle

При использовании автоматического инструментирования OpenTelemetry, особенно в обработках асинхронных задач, существует вероятность потери контекста трассировки. Это часто встречается при работе с методами, такими как CompletableFuture.handle(), когда текущий контекст OpenTelemetry не передается в асинхронные колбеки.

Проблема

В приведённом коде метода future3 вы можете заметить, что при вызове метода handle мы теряем информацию о трассировке:

LOGGER.info("trace response={}", Span.current().getSpanContext().getTraceId()); // <- трасса потеряна

Причина

Когда вызывается CompletableFuture.handle, новый контекст создается в другом потоке, и поэтому информация о текущей трассировке не доступна. Это приводит к тому, что вызовы логирования внутри блока handle не содержат идентификаторы трассировки.

Решение

Для решения этой проблемы необходимо явно захватить текущий контекст OpenTelemetry и использовать его в асинхронном колбеке. Это достигается путем использования класса Context для сохранения текущего состояния и восстановления его в нужном месте.

Пример исправленного кода

Ниже приведён модифицированный код, который сохраняет контекст и восстанавливает его внутри метода handle:

import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;

@GetMapping("/future3")
public CompletableFuture<ResponseEntity<String>> future3() {
    LOGGER.info("future1 ");  
    LOGGER.info("trace={}", Span.current().getSpanContext().getTraceId());

    SqsAsyncClient sqsAsyncClient = SqsAsyncClient.builder().build();

    // Захватываем текущий контекст OpenTelemetry
    Context currentContext = Context.current();

    CompletableFuture<ResponseEntity<String>> aa = sqsAsyncClient
            .sendMessage(build -> build.queueUrl(queueUrl).messageBody("hello"))
            .handle((response, error) -> {
                // Восстанавливаем контекст OpenTelemetry в блоке handle
                try (Scope scope = currentContext.makeCurrent()) {
                    LOGGER.info("trace response={}", Span.current().getSpanContext().getTraceId());
                    LOGGER.info("test response={}", response);
                    return new ResponseEntity<>("success", HttpStatus.OK);
                }
            });

    LOGGER.info("future1_1");
    return aa;
}

Пояснение изменений

  1. Захват контекста: В начале метода мы сохраняем текущий контекст с помощью Context.current().
  2. Восстановление контекста: Внутри блока handle мы используем try (Scope scope = currentContext.makeCurrent()), чтобы установить ранее сохранённый контекст как текущий. Это гарантирует, что любые вызовы OpenTelemetry в этом блоке будут работать с правильной трассировкой.

Заключение

Использование контекста OpenTelemetry в асинхронных обработках — важный шаг к корректному мониторингу и трассировке приложений. Применяя данный подход, вы сможете избежать потери информации о трассировке и улучшить мониторинг производительности вашего приложения.

Если у вас возникнут дополнительные вопросы или потребуется помощь с реализацией, не стесняйтесь задавать их. Удачи в ваших проектах!

Оцените материал
Добавить комментарий

Капча загружается...