В моей демонстрации OpenTelemetry Tracing есть два компонента Spring Boot. Один использует агент Java, и я заметил другое поведение, когда недавно обновил его с v1.x до v2.x. В другом я использую Micrometer Tracing, потому что я компилирую в GraalVM native, а он не может обрабатывать агенты Java.
В этой статье я хочу сравнить эти три подхода: Java agent v1, Java agent v2 и Micrometer Tracing.
Я буду использовать то же самое базовое приложение: простое приложение Spring Boot, написанное на Kotlin. Оно предлагает одну конечную точку.
entry()
intermediate()
WebClient
, замену RestTemplate
, для вызова указанной выше конечной точки.entry()
находит его, она не продолжает работу.
Это преобразуется в следующий код:
@SpringBootApplication class Agent1xApplication @RestController class MicrometerController { private val logger = LoggerFactory.getLogger(MicrometerController::class.java) @GetMapping("/{message}") fun entry(@PathVariable message: String, @RequestHeader("X-done") done: String?) { logger.info("entry: $message") if (done == null) intermediate() } fun intermediate() { logger.info("intermediate") RestClient.builder() .baseUrl("http://localhost:8080/done") .build() .get() .header("X-done", "true") .retrieve() .toBodilessEntity() } }
Для каждой настройки я проверю два этапа: основной этап с включенным OpenTelemetry и этап настройки для создания дополнительных внутренних диапазонов.
Micrometer Tracing происходит от Micrometer , «независимого от поставщика фасада наблюдения за приложениями».
Micrometer Tracing предоставляет простой фасад для самых популярных библиотек трассировки, позволяя вам инструментировать код вашего приложения на основе JVM без привязки к поставщику. Он разработан так, чтобы добавлять мало или вообще не добавлять накладных расходов к вашей деятельности по сбору трассировки, при этом максимально увеличивая переносимость ваших усилий по трассировке.
Чтобы начать работу с Micrometer Tracing, необходимо добавить несколько зависимостей:
org.springframework.boot:spring-boot-starter-actuator
io.micrometer:micrometer-tracing
io.micrometer:micrometer-tracing-bridge-otel
io.opentelemetry:opentelemetry-exporter-otlp
Нам не нужен BOM, поскольку версии уже определены в родительском элементе Spring Boot.
Но нам нужны два параметра конфигурации времени выполнения: куда следует отправлять трассировки и каково имя компонента. Они управляются переменными MANAGEMENT_OTLP_TRACING_ENDPOINT
и SPRING_APPLICATION_NAME
.
services: jaeger: image: jaegertracing/all-in-one:1.55 environment: - COLLECTOR_OTLP_ENABLED=true #1 ports: - "16686:16686" micrometer-tracing: build: dockerfile: Dockerfile-micrometer environment: MANAGEMENT_OTLP_TRACING_ENDPOINT: http://jaeger:4318/v1/traces #2 SPRING_APPLICATION_NAME: micrometer-tracing #3
Вот результат:
Без какой-либо настройки Micrometer создает интервалы при получении и отправке HTTP-запросов.
Фреймворк должен внедрить магию в RestClient
для отправки. Для этого мы должны позволить первому инстанцировать второй:
@SpringBootApplication class MicrometerTracingApplication { @Bean fun restClient(builder: RestClient.Builder) = builder.baseUrl("http://localhost:8080/done").build() }
Мы можем создавать ручные интервалы несколькими способами, один из которых — через сам API OpenTelemetry. Однако для настройки требуется много шаблонного кода. Самый простой способ — это API наблюдения Micrometer. Его главное преимущество — использование единого API, который управляет как метриками , так и трассировками .
Вот обновленный код:
class MicrometerController( private val restClient: RestClient, private val registry: ObservationRegistry ) { @GetMapping("/{message}") fun entry(@PathVariable message: String, @RequestHeader("X-done") done: String?) { logger.info("entry: $message") val observation = Observation.start("entry", registry) if (done == null) intermediate(observation) observation.stop() } fun intermediate(parent: Observation) { logger.info("intermediate") val observation = Observation.createNotStarted("intermediate", registry) .parentObservation(parent) .start() restClient.get() .header("X-done", "true") .retrieve() .toBodilessEntity() observation.stop() } }
Добавленные вызовы наблюдения отражаются на сгенерированных трассах:
Альтернативой Micrometer Tracing является универсальный OpenTelemetry Java Agent . Его главное преимущество в том, что он не влияет ни на код, ни на разработчиков; агент — это чисто runtime-область.
java -javaagent:opentelemetry-javaagent.jar agent-one-1.0-SNAPSHOT.jar
Агент придерживается конфигурации OpenTelemetry с переменными среды:
services: agent-1x: build: dockerfile: Dockerfile-agent1 environment: OTEL_EXPORTER_OTLP_ENDPOINT: http://jaeger:4317 #1 OTEL_RESOURCE_ATTRIBUTES: service.name=agent-1x #2 OTEL_METRICS_EXPORTER: none #3 OTEL_LOGS_EXPORTER: none #4 ports: - "8081:8080"
/v1/traces
Без дополнительной настройки получаем следующие трассировки:
Агент автоматически отслеживает запросы, как полученные, так и отправленные, а также функции, отмеченные аннотациями, связанными со Spring . Трассировки правильно вложены друг в друга в соответствии со стеком вызовов. Чтобы трассировать дополнительные функции, нам нужно добавить зависимость в нашу кодовую базу, io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations
. Теперь мы можем аннотировать ранее неотслеживаемые функции с помощью аннотации @WithSpan
.
Часть value()
управляет меткой трассировки, а kind
транслируется как атрибут span.kind
. Если значение установлено в пустую строку, что является значением по умолчанию, оно выводит имя функции. Для моих целей значения по умолчанию вполне подходят.
@WithSpan fun intermediate() { logger.info("intermediate") RestClient.builder() .baseUrl("http://localhost:8080/done") .build() .get() .header("X-done", "true") .retrieve() .toBodilessEntity() }
Это дает ожидаемый новый intermediate()
след:
OpenTelemetry выпустила новую основную версию агента в январе этого года. Я обновил свою демоверсию с ее помощью; теперь трассировки создаются только тогда, когда приложение получает и отправляет запросы.
Как и в предыдущей версии, мы можем добавлять трассировки с помощью аннотации @WithSpan
. Единственное отличие в том, что мы также должны аннотировать функцию entry()
. По умолчанию она не трассируется.
Spring стал успешным по двум причинам: он упростил сложные решения, т. е . EJBs 2, и предоставил уровень абстракции по сравнению с конкурирующими библиотеками. Micrometer Tracing начинался как уровень абстракции по сравнению с Zipkin и Jaeger, и это имело смысл. Этот аргумент становится спорным, поскольку OpenTelemetry поддерживается большинством библиотек на всех языках программирования и сборщиками трассировок. API наблюдения по-прежнему является значительным преимуществом Micrometer Tracing, поскольку он использует единый API по сравнению с метриками и трассировками.
На стороне Java Agent конфигурация OpenTelemetry одинакова для всех технологических стеков и библиотек - переменные окружения. Я был немного разочарован, когда обновился с v1 до v2, так как новый агент не поддерживает Spring: функции, аннотированные Spring, по умолчанию не трассируются.
В конце концов, это мудрое решение. Гораздо лучше быть явным относительно нужных вам диапазонов, чем удалять те, которые вы не хотите видеть.
Спасибо Джонатану Иванову за его помощь и отзыв .
Полный исходный код этой статьи можно найти на GitHub:
Чтобы пойти дальше:
Первоначально опубликовано в A Java Geek 3 августа 2024 г.