Спостережуваність Spring Boot у 2026: OpenTelemetry, розподілене трасування та питання на співбесідах

Повний посібник зі спостережуваності Spring Boot з OpenTelemetry та Micrometer Tracing. Налаштування розподіленого трасування, Observation API, експорт OTLP та підготовка до технічних співбесід.

Спостережуваність Spring Boot з розподіленим трасуванням OpenTelemetry

Спостережуваність Spring Boot поєднує логування, метрики та розподілене трасування в єдину систему, що показує потік запитів через мікросервіси. Починаючи з Spring Boot 3, фреймворк прийняв Micrometer Tracing (замінивши Spring Cloud Sleuth) та представив Observation API, що забезпечує єдину точку інструментації, яка генерує як метрики, так і трейси.

Патерн Observation API

Spring Boot рекомендує використовувати Observation.observe() замість прямого виклику OpenTelemetry. Один виклик інструментації створює метрики через Micrometer та трейси через міст OpenTelemetry, зменшуючи дублювання коду та забезпечуючи узгоджене іменування тегів між сигналами.

Як Observation API поєднує Micrometer та OpenTelemetry

Observation API діє як фасад над метриками та трасуванням. Коли код викликає Observation.createNotStarted(), Spring Boot направляє спостереження до зареєстрованих обробників: MeterObservationHandler для метрик Micrometer та TracingObservationHandler для розподіленого трасування. Ця архітектура означає, що одноразова інструментація експортує дані всюди.

Залежність-міст micrometer-tracing-bridge-otel з'єднує Micrometer Tracing з SDK OpenTelemetry. Трейси проходять через SdkTracerProvider OpenTelemetry та експортуються через OTLP до бекендів, таких як Jaeger, Tempo або будь-який колектор, сумісний з OpenTelemetry.

ObservabilityConfig.javajava
@Configuration
public class ObservabilityConfig {

    @Bean
    public ObservationRegistryCustomizer<ObservationRegistry> addLowCardinalityTags() {
        return registry -> registry.observationConfig()
            .observationHandler(new ObservationTextPublisher()); // Логує спостереження в консоль
    }
}

Наведена конфігурація реєструє ObservationHandler, який логує кожне спостереження. У продакшені автоматично налаштовані обробники надсилають дані до реєстрів Micrometer та експортерів OpenTelemetry без додаткового коду.

Необхідні залежності для Spring Boot 3.4 Tracing

Spring Boot 3.4 вимагає явних залежностей для розподіленого трасування. spring-boot-starter-actuator надає Observation API, але трасування потребує моста OpenTelemetry та експортера.

xml
<!-- pom.xml -->
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>io.micrometer</groupId>
        <artifactId>micrometer-tracing-bridge-otel</artifactId>
    </dependency>
    <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-exporter-otlp</artifactId>
    </dependency>
</dependencies>

Артефакт micrometer-tracing-bridge-otel з'єднує Micrometer Tracing з API OpenTelemetry. opentelemetry-exporter-otlp надсилає спани до колекторів, використовуючи протокол OTLP через HTTP або gRPC. Spring Boot керує вирівнюванням версій через своє управління залежностями.

Налаштування експорту OTLP до Jaeger або Tempo

Spring Boot автоматично налаштовує OtlpHttpSpanExporter, коли присутня залежність OTLP. Експортер надсилає трейси до ендпоінта, вказаного в application.yml.

yaml
# application.yml
management:
  tracing:
    sampling:
      probability: 1.0  # 100% семплювання для розробки, зменште у продакшені
  otlp:
    tracing:
      endpoint: http://localhost:4318/v1/traces
  opentelemetry:
    resource-attributes:
      service.name: order-service
      deployment.environment: staging

resource-attributes додають метадані до кожного спана. Бекенди, такі як Grafana Tempo та Jaeger, використовують ці атрибути для групування трейсів за сервісом та середовищем. Встановлення sampling.probability на 1.0 захоплює всі запити під час розробки, але продакшен-системи зазвичай семплюють від 1% до 10% для контролю витрат на зберігання.

Створення власних спостережень з тегами низької та високої кардинальності

Спостереження підтримують два типи тегів: низької кардинальності для метрик (обмежені значення, як HTTP-методи або коди статусу) та високої кардинальності для трейсів (необмежені значення, як ID користувача або ID запиту).

PaymentService.javajava
@Service
public class PaymentService {

    private final ObservationRegistry observationRegistry;

    public PaymentService(ObservationRegistry observationRegistry) {
        this.observationRegistry = observationRegistry;
    }

    public PaymentResult processPayment(PaymentRequest request) {
        return Observation.createNotStarted("payment.process", observationRegistry)
            .lowCardinalityKeyValue("payment.method", request.getMethod().name())  // enum: CARD, BANK_TRANSFER
            .lowCardinalityKeyValue("currency", request.getCurrency())              // обмежено: USD, EUR, GBP
            .highCardinalityKeyValue("payment.id", request.getPaymentId())          // унікальний для запиту
            .highCardinalityKeyValue("customer.id", request.getCustomerId())        // висока кардинальність
            .observe(() -> executePayment(request));
    }

    private PaymentResult executePayment(PaymentRequest request) {
        // Виклик платіжного шлюзу
        return new PaymentResult(true, "TXN-" + UUID.randomUUID());
    }
}

Теги низької кардинальності з'являються як у метриках, так і в трейсах. Теги високої кардинальності з'являються лише в трейсах, оскільки метрики з необмеженими вимірами вибухають сховище. Це розрізнення запобігає бомбам кардинальності в Prometheus, зберігаючи багаті деталі трейсів.

Питання на співбесіді: кардинальність тегів

Інтерв'юери часто запитують, чому деякі теги слід виключати з метрик. Відповідь стосується кардинальності: тег ID користувача на метриці створює один часовий ряд на користувача, потенційно мільйони рядів. Бекенди моніторингу мають проблеми з високою кардинальністю, що призводить до вичерпання пам'яті та повільних запитів. Трейси обробляють високу кардинальність через семплювання, що робить їх правильним місцем для ідентифікаторів, специфічних для запиту.

Готовий до співбесід з Spring Boot?

Практикуйся з нашими інтерактивними симуляторами, flashcards та технічними тестами.

Поширення контексту трейса через асинхронні межі

Розподілене трасування вимагає поширення контексту. HTTP-заголовки переносять ID трейсів між сервісами, але асинхронні операції, такі як методи @Async або ланцюжки CompletableFuture, можуть втратити контекст, якщо не налаштовані.

Spring Boot 3.4 забезпечує автоматичне поширення для реактивних потоків та методів @Async при відповідному налаштуванні:

yaml
# application.yml
spring:
  reactor:
    context-propagation: auto
  task:
    execution:
      propagate-context: true

Для власних бінів TaskExecutor їх слід обгорнути в ContextPropagatingTaskDecorator:

AsyncConfig.javajava
@Configuration
@EnableAsync
public class AsyncConfig {

    @Bean
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(50);
        executor.setTaskDecorator(new ContextPropagatingTaskDecorator());
        executor.initialize();
        return executor;
    }
}

Декоратор копіює поточну Observation та контекст трейса в асинхронний потік. Без нього спани, розпочаті в асинхронних завданнях, не мають батьківського спана, що порушує дерево трейсів.

Використання анотацій @Observed та @NewSpan

Spring Boot 3.4 підтримує декларативну спостережуваність через анотації. Увімкніть обробку анотацій, додавши AspectJ weaver:

xml
<!-- pom.xml -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
yaml
# application.yml
management:
  observations:
    annotations:
      enabled: true
InventoryService.javajava
@Service
public class InventoryService {

    @Observed(name = "inventory.check", contextualName = "checkStock")
    public StockLevel checkStock(String productId) {
        // Запит до бази даних
        return new StockLevel(productId, 42);
    }

    @NewSpan("inventory.reserve")  // Створює дочірній спан під поточним трейсом
    public ReservationResult reserveStock(String productId, int quantity) {
        // Оновлення інвентарю
        return new ReservationResult(true, "RES-" + System.currentTimeMillis());
    }
}

@Observed створює як метрику (таймер), так і спан. @NewSpan створює лише спан, корисно коли операція вже має метрики в іншому місці. Обидві анотації автоматично обробляють запис винятків та статус спана.

Автоматично інструментовані компоненти в Spring Boot 3.4

Spring Boot 3.4 автоматично інструментує кілька компонентів без змін у коді:

КомпонентНазва спостереженняЩо захоплює
Spring MVChttp.server.requestsШлях запиту, метод, статус, виняток
WebClienthttp.client.requestsВихідні HTTP-виклики
RestClienthttp.client.requestsВихідні HTTP-виклики (Spring 6.1+)
Spring Kafkaspring.kafka.listenerГрупа споживачів, топік, партиція
Spring Data JPAspring.data.repositoryМетод репозиторію, час запиту
Scheduled Tasksspring.schedulingНазва завдання, час виконання

Документація Spring Boot Actuator перелічує всі автоматично інструментовані компоненти. Сторонні бібліотеки, такі як Datasource Micrometer, додають трасування JDBC-запитів.

Фільтрація спостережень для зменшення шуму

Перевірки здоров'я, readiness probes та статичні ресурси генерують шум у трейсах. Їх можна відфільтрувати за допомогою предикатів:

ObservationFilterConfig.javajava
@Configuration
public class ObservationFilterConfig {

    @Bean
    public ObservationPredicate noHealthChecks() {
        return (name, context) -> !name.equals("http.server.requests")
            || !isHealthEndpoint(context);
    }

    private boolean isHealthEndpoint(Observation.Context context) {
        if (context instanceof ServerRequestObservationContext http) {
            String path = http.getCarrier().getRequestURI();
            return path.startsWith("/actuator/health") || path.startsWith("/actuator/ready");
        }
        return false;
    }
}

Альтернативно можна вимкнути спостереження за назвою в конфігурації:

yaml
# application.yml
management:
  observations:
    enable:
      spring.security: false  # Вимкнути спостереження Spring Security
      http.server.requests.actuator: false  # Власна назва предиката
Контекст співбесіди: семплювання vs фільтрація

Семплювання зменшує відсоток трейсів, що збираються з усіх ендпоінтів. Фільтрація повністю видаляє певні ендпоінти. Використовуйте фільтрацію для ендпоінтів, що ніколи не надають діагностичної цінності (перевірки здоров'я, скрапінг метрик). Використовуйте семплювання для контролю витрат при збереженні репрезентативних даних.

Кореляція логів з ID трейсів

Spring Boot 3.4 автоматично додає ID трейса та спана до MDC (Mapped Diagnostic Context), коли активний Micrometer Tracing. Logback та Log4j2 можуть включати ці ID у вивід логів:

xml
<!-- logback-spring.xml -->
<configuration>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - traceId=%X{traceId} spanId=%X{spanId} - %msg%n</pattern>
        </encoder>
    </appender>
    <root level="INFO">
        <appender-ref ref="CONSOLE" />
    </root>
</configuration>

З цим патерном кожен рядок логу містить ID трейса. Пошук логів за ID трейса повертає всі повідомлення з одного запиту з усіх сервісів, корелюючи логи зі спанами в Jaeger або Tempo.

Поширені питання на співбесідах щодо спостережуваності Spring Boot

Інтерв'юери оцінюють як концептуальне розуміння, так і практичний досвід зі спостережуваністю. Ці питання часто з'являються на позиціях senior backend.

П: Яка різниця між Micrometer Tracing та OpenTelemetry?

Micrometer Tracing - це vendor-neutral API для розподіленого трасування, подібно до того, як Micrometer абстрагує метрики. OpenTelemetry - це конкретна реалізація та протокол. Spring Boot використовує Micrometer Tracing як API та з'єднує з OpenTelemetry для експорту через micrometer-tracing-bridge-otel. Це розшарування дозволяє застосункам змінювати бекенди без змін у коді.

П: Чому Spring рекомендує Observation API замість прямої інструментації OpenTelemetry?

Observation API надає єдину точку інструментації, що генерує як метрики, так і трейси. Прямі виклики OpenTelemetry створюють лише трейси. Використання Observation.observe() генерує метрику таймера та спан з одного коду, зменшуючи дублювання та забезпечуючи узгоджене іменування.

П: Як налагодити трейс, що показує розрив між спанами?

Розриви вказують на відсутню інструментацію або порушене поширення контексту. Перевірте, чи код використовує асинхронні операції без ContextPropagatingTaskDecorator. Переконайтеся, що HTTP-клієнти інструментовані (WebClient, RestClient або вручну обгорнутий RestTemplate). Для черг повідомлень підтвердіть, що заголовки трейсів поширюються через властивості повідомлень.

П: Що відбувається, якщо додати тег високої кардинальності до метрики?

Кожне унікальне значення тега створює новий часовий ряд. Тег ID користувача з мільйонами користувачів створює мільйони рядів, вичерпуючи пам'ять у Prometheus або інших TSDB-бекендах. Рішення - використовувати highCardinalityKeyValue() замість lowCardinalityKeyValue(), що додає тег лише до трейсів, де очікується висока кардинальність.

Побудова пайплайна розподіленого трасування з Docker Compose

Локальний стек спостережуваності допомагає валідувати інструментацію перед деплоєм у продакшен. Цей приклад використовує OpenTelemetry Collector та Jaeger:

yaml
# docker-compose.yml
services:
  otel-collector:
    image: otel/opentelemetry-collector-contrib:0.102.0
    command: ["--config", "/etc/otel-collector-config.yml"]
    volumes:
      - ./otel-collector-config.yml:/etc/otel-collector-config.yml
    ports:
      - "4318:4318"   # OTLP HTTP
      - "4317:4317"   # OTLP gRPC

  jaeger:
    image: jaegertracing/jaeger:2.3
    ports:
      - "16686:16686"  # UI
    environment:
      - COLLECTOR_OTLP_ENABLED=true

  app:
    build: .
    environment:
      - MANAGEMENT_OTLP_TRACING_ENDPOINT=http://otel-collector:4318/v1/traces
    depends_on:
      - otel-collector
yaml
# otel-collector-config.yml
receivers:
  otlp:
    protocols:
      http:
        endpoint: 0.0.0.0:4318
      grpc:
        endpoint: 0.0.0.0:4317

processors:
  batch:
    timeout: 1s
    send_batch_size: 1024

exporters:
  otlp/jaeger:
    endpoint: jaeger:4317
    tls:
      insecure: true

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlp/jaeger]

Колектор отримує спани від застосунку Spring Boot та пересилає їх до Jaeger. Ця архітектура дозволяє додавати експортери (Tempo, Zipkin, хмарні бекенди) без зміни коду застосунку.

Починай практикувати!

Перевір свої знання з нашими симуляторами співбесід та технічними тестами.

Ключові висновки щодо спостережуваності Spring Boot

  • Observation API об'єднує метрики та трасування: один виклик Observation.observe() створює як метрику таймера, так і спан, усуваючи подвійну інструментацію.
  • Додавання micrometer-tracing-bridge-otel та opentelemetry-exporter-otlp вмикає експорт OTLP. Spring Boot 3.4 автоматично налаштовує HTTP-експортер з ендпоінтом з management.otlp.tracing.endpoint.
  • Використовуйте lowCardinalityKeyValue() для вимірів, що з'являються в метриках (обмежені значення). Використовуйте highCardinalityKeyValue() для даних лише в трейсах (ID користувачів, ID запитів).
  • Увімкніть поширення контексту для асинхронного коду за допомогою spring.task.execution.propagate-context=true та обгортайте власні executor'и в ContextPropagatingTaskDecorator.
  • Фільтрація шумних ендпоінтів, таких як /actuator/health, за допомогою бінів ObservationPredicate зберігає трейси зосередженими на бізнес-операціях.
  • Патерни логів повинні включати %X{traceId} для кореляції рядків логів з розподіленими трейсами у вашому бекенді спостережуваності.
  • Питання на співбесідах щодо спостережуваності зосереджуються на кардинальності, помилках поширення контексту та різниці між Observation API та прямим використанням OpenTelemetry.
Щоденний виклик

Чи знайдеш ти помилку в Spring Boot?

Справжній фрагмент коду, прихована помилка, одна спроба на день. Щоб спробувати, акаунт не потрібен.

Anthony Fillion-Maillet

Автор:

Anthony Fillion-Maillet

Засновник SharpSkill

Fullstack-розробник понад 10 років. Керує SharpSkill і відповідає за все, що тут публікується.

Оновлено 19 вересня 2026 р.

Теги

#spring-boot
#observability
#opentelemetry
#distributed-tracing
#micrometer

Поділитися

Пов'язані статті