# Спостережуваність Spring Boot у 2026: OpenTelemetry, розподілене трасування та питання на співбесідах > Повний посібник зі спостережуваності Spring Boot з OpenTelemetry та Micrometer Tracing. Налаштування розподіленого трасування, Observation API, експорт OTLP та підготовка до технічних співбесід. - Published: 2026-09-19 - Updated: 2026-09-19 - Author: Anthony Fillion-Maillet - Tags: spring-boot, observability, opentelemetry, distributed-tracing, micrometer - Reading time: 12 min --- Спостережуваність 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. ```java // ObservabilityConfig.java @Configuration public class ObservabilityConfig { @Bean public ObservationRegistryCustomizer 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 org.springframework.boot spring-boot-starter-actuator io.micrometer micrometer-tracing-bridge-otel io.opentelemetry opentelemetry-exporter-otlp ``` Артефакт `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](https://grafana.com/oss/tempo/) та [Jaeger](https://www.jaegertracing.io/), використовують ці атрибути для групування трейсів за сервісом та середовищем. Встановлення `sampling.probability` на `1.0` захоплює всі запити під час розробки, але продакшен-системи зазвичай семплюють від 1% до 10% для контролю витрат на зберігання. ## Створення власних спостережень з тегами низької та високої кардинальності Спостереження підтримують два типи тегів: низької кардинальності для метрик (обмежені значення, як HTTP-методи або коди статусу) та високої кардинальності для трейсів (необмежені значення, як ID користувача або ID запиту). ```java // PaymentService.java @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 користувача на метриці створює один часовий ряд на користувача, потенційно мільйони рядів. Бекенди моніторингу мають проблеми з високою кардинальністю, що призводить до вичерпання пам'яті та повільних запитів. Трейси обробляють високу кардинальність через семплювання, що робить їх правильним місцем для ідентифікаторів, специфічних для запиту. ## Поширення контексту трейса через асинхронні межі Розподілене трасування вимагає поширення контексту. HTTP-заголовки переносять ID трейсів між сервісами, але асинхронні операції, такі як методи `@Async` або ланцюжки `CompletableFuture`, можуть втратити контекст, якщо не налаштовані. Spring Boot 3.4 забезпечує автоматичне поширення для реактивних потоків та методів `@Async` при відповідному налаштуванні: ```yaml # application.yml spring: reactor: context-propagation: auto task: execution: propagate-context: true ``` Для власних бінів `TaskExecutor` їх слід обгорнути в `ContextPropagatingTaskDecorator`: ```java // AsyncConfig.java @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 org.springframework.boot spring-boot-starter-aop ``` ```yaml # application.yml management: observations: annotations: enabled: true ``` ```java // InventoryService.java @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 MVC | `http.server.requests` | Шлях запиту, метод, статус, виняток | | WebClient | `http.client.requests` | Вихідні HTTP-виклики | | RestClient | `http.client.requests` | Вихідні HTTP-виклики (Spring 6.1+) | | Spring Kafka | `spring.kafka.listener` | Група споживачів, топік, партиція | | Spring Data JPA | `spring.data.repository` | Метод репозиторію, час запиту | | Scheduled Tasks | `spring.scheduling` | Назва завдання, час виконання | [Документація Spring Boot Actuator](https://docs.spring.io/spring-boot/reference/actuator/observability.html) перелічує всі автоматично інструментовані компоненти. Сторонні бібліотеки, такі як [Datasource Micrometer](https://github.com/jdbc-observations/datasource-micrometer), додають трасування JDBC-запитів. ## Фільтрація спостережень для зменшення шуму Перевірки здоров'я, readiness probes та статичні ресурси генерують шум у трейсах. Їх можна відфільтрувати за допомогою предикатів: ```java // ObservationFilterConfig.java @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 %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - traceId=%X{traceId} spanId=%X{spanId} - %msg%n ``` З цим патерном кожен рядок логу містить 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](https://opentelemetry.io/docs/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. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/uk/blog/spring-boot/spring-boot-observability-opentelemetry-distributed-tracing