Перейти к содержанию
V1 V2

Наблюдаемость по проектированию: один запрос через весь стек Kora

21 августа 2026

Наблюдаемость часто добавляется к бэкенд-сервису уже после того, как приложение заработало. Сначала реализуется HTTP-эндпоинт, затем репозиторий, затем исходящий клиент, и только после первого серьёзного инцидента кто-то просит метрики задержки, распределённую трассировку, структурированные логи, пробы готовности и стратегию завершения работы, которая не убивает запросы в полёте. Такая последовательность понятна, но с точки зрения эксплуатации она обратна правильной. К тому моменту, когда производственный сервис начинает отказывать, информация, необходимая для объяснения сбоя, уже должна существовать.

Фреймворк Kora придерживается другого подхода. Наблюдаемость — часть архитектуры сервиса с самого начала. Фреймворк интегрирует телеметрию в модули, метрики Micrometer, трассировку OpenTelemetry, структурированное логирование с корреляцией по трассировке, пробы живости и готовности на отдельном системном порту, а также поведение жизненного цикла, включая корректное завершение работы.

Важная идея не просто в том, что Kora «поддерживает метрики» или «поддерживает трассировку». Почти каждый современный фреймворк это делает. Более интересное свойство в том, что эти сигналы выстраиваются в линию с реальным путём исполнения приложения.

Рассмотрим один запрос:

span HTTP-сервера
      ↓
сервис приложения
      ↓
span HTTP-клиента
      ↓
span репозитория
      ↓
база данных

Тот же запрос также вносит вклад в агрегированные метрики:

задержка HTTP-запроса
распределение статусов HTTP
задержка исходящего клиента
длительность запроса к базе данных
насыщение пула соединений
метрики JVM / процесса
бизнес-счётчики и таймеры

а его логи могут нести ту же идентичность трассировки:

traceId
spanId
структурированные поля событий
бизнес-контекст

Результат — не три несвязанных продукта наблюдаемости, прикрученных к одному сервису. Это одна операционная модель, наблюдаемая с трёх дополняющих друг друга направлений. Метрики говорят вам, что что-то не так. Трассировка говорит вам, куда ушли время или сбой. Логи говорят вам, что код решил, пока это происходило. Пробы говорят платформе, должен ли этот процесс получать трафик или его вообще следует перезапустить. Корректное завершение работы завершает модель, определяя, что происходит, когда процесс намеренно выводится из обслуживания.

Эта статья прослеживает один запрос через стек Kora и использует этот путь, чтобы объяснить, как наблюдаемость должна работать в производственном бэкенде.

Наблюдаемость — это контракт времени выполнения

У производственного сервиса два контракта. Первый — бизнес-контракт: эндпоинты, полезные нагрузки, коды статуса, семантика и ожидания по задержке. Второй — операционный. Платформа и операторы должны знать, жив ли процесс, готов ли он, сколько запросов обслуживает, сколько времени эти запросы занимают, какие зависимости медленные, какой запрос к базе данных упал, какой запрос породил данную строку лога и завершает ли завершение работы безопасный слив трафика.

Сервис, который удовлетворяет свой бизнес-API, но не может ответить на эти вопросы, неполон с точки зрения эксплуатации.

Модель наблюдаемости Kora рассматривает эти возможности как первоклассные модули и задачи жизненного цикла, а не как необязательные утилиты для отладки. Это важно, потому что наблюдаемость наиболее полезна, когда она структурно согласована. Если каждый контроллер изобретает собственное логирование, каждый репозиторий изобретает собственные имена метрик, каждый HTTP-клиент использует другую обёртку трассировки, а готовность живёт на том же публичном порту, что и пользовательский трафик, сервис технически излучает сигналы, но у него нет архитектуры наблюдаемости.

Kora пытается предоставить эту архитектуру во всех модулях.

Один запрос — лучший способ понять стек

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

Представьте API-эндпоинт:

GET /users/{id}/dashboard

Запрос входит в HTTP-сервер Kora. Затем сервис загружает информацию о пользователе из PostgreSQL, вызывает удалённый сервис рекомендаций, объединяет результаты и возвращает JSON.

Путь времени выполнения:

Клиент
  ↓
HTTP-сервер Kora
  ↓
DashboardService
  ├── UserRepository
  │      ↓
  │   PostgreSQL
  │
  └── RecommendationClient
         ↓
      удалённый сервис

Теперь добавьте телеметрию:

SPAN HTTP-СЕРВЕРА
  GET /users/{id}/dashboard
        ↓
   DashboardService
        ↓
   ┌───────────────┐
   │               │
SPAN БД-КЛИЕНТА   SPAN HTTP-КЛИЕНТА
SELECT user     GET /recommendations
   │               │
PostgreSQL      удалённый сервис

Трассировка даёт причинно-следственную картину. Метрики дают агрегированное поведение для каждого ребра. Логи объясняют решения приложения, происходящие внутри этих спанов. Эта модель, центрированная на запросе, — фундамент полезной наблюдаемости.

HTTP-сервер — первая граница наблюдаемости

Входящий HTTP-сервер — это место, где сервис впервые получает достаточно информации, чтобы идентифицировать операцию. Как минимум фреймворк знает HTTP-метод, маршрут, начало запроса, завершение запроса, код статуса, ошибку и длительность. Этого достаточно, чтобы произвести и span трассировки, и HTTP-метрики.

Span сервера должен представлять:

GET /users/{id}/dashboard

а не буквальный URL:

GET /users/894192/dashboard

Это различие важно. Шаблоны маршрутов ограничены; сырые идентификаторы — нет. Ограниченное имя маршрута хорошо подходит для имён спанов, меток метрик, дашбордов и агрегации. Идентификатор пользователя принадлежит другому месту.

Это подводит к одному из самых важных правил наблюдаемости: агрегируйте по измерениям с низкой кардинальностью и прикрепляйте контекст с высокой кардинальностью только там, где бэкенд спроектирован его обрабатывать.

Метрики и трассировки наблюдают одну операцию по-разному

Для одного входящего маршрута HTTP-сервер может вносить метрику, концептуально похожую на:

http.server.request.duration

с такими измерениями, как:

method=GET
route=/users/{id}/dashboard
status=200

и span трассировки с именем:

GET /users/{id}/dashboard

Эти сигналы отвечают на разные вопросы. Метрика может ответить: «Какова p95-задержка этого маршрута за последние тридцать минут?» Трассировка может ответить: «Почему этот конкретный запрос на 1,8 секунды занял 1,8 секунды?»

Ошибка — ожидать, что один сигнал заменит другой. Бэкенд трассировки — не хорошая замена дешёвым агрегированным гистограммам задержки. Дашборд метрик — не замена причинно-следственному дереву на уровне запроса. Здоровому производственному стеку нужны оба.

Идентификатор трассировки становится операционной идентичностью запроса

Как только HTTP-сервер создаёт или продолжает трассировку, у запроса появляется операционная идентичность.

Концептуально:

traceId = 79d65...

Каждый дочерний span принадлежит этой трассировке. Структурированные логи, испускаемые, пока трассировка активна, могут нести:

traceId
spanId

Это даёт операторам ключ соединения.

Предположим, Grafana показывает всплеск частоты HTTP 500. Обозреватель трассировок показывает один упавший запрос. Span базы данных внутри этой трассировки показывает таймаут пула соединений. Строка лога с тем же идентификатором трассировки говорит, что запасной вариант был намеренно отключён, потому что операция чувствительна к записи.

Это больше не независимые улики. Они принадлежат одному исполнению.

Именно эта корреляция делает наблюдаемость операционно мощной.

Распространение контекста — скрытый хребет

Распределённая трассировка выглядит простой на диаграмме:

span сервера
  ↓
span клиента
  ↓
span удалённого сервера

но важный механизм — распространение контекста. Текущий контекст трассировки должен выживать при вызовах методов сервиса, исполнении виртуальных потоков, вызовах репозитория, исходящих HTTP-вызовах и асинхронных границах, где это применимо. Затем исходящий клиент должен внедрять контекст трассировки в заголовки запроса, чтобы нижестоящий сервис мог продолжить ту же трассировку.

Без распространения каждый слой создаёт свой собственный изолированный корневой span:

Трассировка A: входящий HTTP
Трассировка B: база данных
Трассировка C: исходящий HTTP

Это не распределённая трассировка. Это распределённая генерация спанов.

Интеграции телеметрии Kora важны, потому что модули фреймворка участвуют в одной модели контекста, а не заставляют каждый бизнес-метод вручную передавать идентификаторы трассировки.

Сервисный слой не должен нуждаться в телеметрической обвязке повсюду

Хорошо спроектированный стек наблюдаемости должен делать инфраструктурные операции наблюдаемыми автоматически. Бизнес-код не должен снова и снова вызывать startHttpSpan(), recordHttpMetric(), putTraceIdIntoMdc() или дублировать логику тайминга репозитория вокруг каждой операции.

Модульная телеметрия Kora предназначена для автоматической обработки инфраструктурных границ. HTTP-сервер знает, когда запрос начинается и заканчивается. HTTP-клиент знает, когда исходящий вызов начинается и заканчивается. Интеграция базы данных знает, когда запрос получает соединение, начинает исполнение, завершается успехом или сбоем.

Эти компоненты находятся в лучшей позиции для испускания стандартизированной инфраструктурной телеметрии.

Бизнес-код должен добавлять телеметрию только там, где фреймворк не может знать семантическое значение.

Это разделение ответственности важно.

Инфраструктурная телеметрия против бизнес-телеметрии

Телеметрия фреймворка может знать, что HTTP-запрос завершился за 42 мс, запрос к базе данных занял 8 мс или исходящий HTTP-вызов вернул 503. Она не может знать, что платёж был одобрен, заказ отклонён, потому что оценка риска превысила порог, клиент повысил подписку или резервирование на складе прошло успешно.

Это бизнес-семантика.

Поэтому у сильного сервиса на Kora два слоя телеметрии. Первый — предоставляемая фреймворком инфраструктурная телеметрия. Второй — определяемая приложением бизнес-телеметрия.

Например:

HTTP:
GET /users/{id}

Бизнес:
subscription.lookup

База данных:
UserRepository.findById

Исходящий:
GET /billing/profile

Это создаёт полезную трассировку без инструментирования каждой функции. Цель — не максимальное количество спанов. Цель — максимальная объяснительная ценность.

Не превращайте каждый метод в span

Распределённая трассировка становится менее полезной, когда каждый крошечный вспомогательный метод создаёт span.

Плохая трассировка может выглядеть так:

span HTTP
  ↓
span контроллера
  ↓
span сервиса
  ↓
span валидации
  ↓
span маппинга
  ↓
span форматирования
  ↓
span репозитория

Это создаёт визуальный шум и стоимость телеметрии, не добавляя почти никакой операционной информации.

Span обычно должен представлять осмысленную границу операции. Хорошие кандидаты — входящие операции протокола, исходящие сетевые вызовы, запросы к базе данных, обработка сообщений, значимые бизнес-операции и дорогие внутренние вычисления. Тривиальным геттерам и функциям маппинга обычно не нужны спаны.

Kora может автоматически инструментировать инфраструктурные границы и предоставляет API трассировки для осмысленных бизнес-спанов. Это хороший баланс.

Ручные бизнес-спаны должны объяснять предметную область

Предположим, запрос выполняет бизнес-операцию, называемую:

user.dashboard.build

Ручной дочерний span может её представлять:

HTTP-СЕРВЕР
GET /users/{id}/dashboard
  ↓
БИЗНЕС
user.dashboard.build
  ├── БД-КЛИЕНТ
  │   UserRepository.findById
  │
  └── HTTP-КЛИЕНТ
      GET /recommendations

Бизнес-span даёт трассировке семантическую структуру. Теперь оператор может различать границу транспорта, бизнес-операцию и инфраструктурные зависимости, не инструментируя каждый внутренний метод.

Помощники трассировки Kora полезны, потому что могут вкладывать ручной span под текущий активный span запроса, а не заставлять код приложения вручную восстанавливать контекст.

Атрибуты спана — это не теги метрик

Предположим, у запроса есть:

userId=982374723

Добавление этого в span может быть разумным, когда это операционно полезно:

user.id=982374723

Добавление этого как тега метрики может быть катастрофичным. Бэкенд метрик может создать один временной ряд на каждый идентификатор пользователя.

Полезное правило:

ограниченные измерения
→ метрики

значения, специфичные для запроса / с высокой кардинальностью
→ трассировки или логи

Kora может предоставить обвязку, но команды приложения всё равно нуждаются в дисциплине кардинальности.

Метрики отвечают на вопросы о популяциях

Метрики — это агрегированные сигналы. Они описывают популяции операций с течением времени.

Для входящего HTTP-маршрута полезные метрики включают частоту запросов, частоту ошибок, распределение задержки, активные запросы и распределение статусов. Для исходящего клиента полезные метрики включают частоту запросов, статус ответа, задержку и таймауты. Для PostgreSQL полезные метрики включают длительность запроса, состояние пула соединений и давление при получении соединения. Для JVM полезные метрики включают CPU, кучу, GC, потоки, файловые дескрипторы и время работы.

Дашборд, построенный из этих сигналов, говорит вам, как сервис ведёт себя как система.

Гистограммы важнее средних значений

Средние значения скрывают хвостовую задержку.

Рассмотрим десять запросов:

9 запросов = 10 мс
1 запрос  = 910 мс

Среднее — 100 мс, но ни один пользователь на самом деле не испытал 100 мс. Большинство увидели 10 мс. Один — почти секунду.

Производственные системы заботятся о распределениях.

Вот почему метрикам задержки нужны корзины гистограмм, ориентированные на SLO, и анализ перцентилей. Вопросы вроде «какова p95?» или «какой процент завершается быстрее 100 мс?» гораздо полезнее, чем «какова средняя задержка?»

Micrometer даёт Kora сильную поверхность метрик для такого рода операционного анализа.

Корзины SLO должны отражать реальные цели

Корзины по умолчанию — отправная точка, а не универсальная истина. Если у внутреннего эндпоинта поиска SLO 50 мс, гистограмма, различающая только одну секунду, пять секунд и десять секунд, операционно слаба.

Полезные корзины могут быть:

10 мс
25 мс
50 мс
100 мс
250 мс

для быстрого эндпоинта, в то время как пакетному API могут понадобиться совершенно другие пороги.

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

HTTP-клиент — следующая крупная граница

Теперь продолжим запрос. Сервис дашборда вызывает:

RecommendationClient.getRecommendations(userId)

Телеметрия HTTP-клиента Kora может наблюдать эту исходящую операцию.

Трассировка становится:

HTTP-СЕРВЕР
GET /users/{id}/dashboard
  ↓
HTTP-КЛИЕНТ
GET /recommendations

Span клиента должен быть дочерним для текущей трассировки запроса. Затем контекст трассировки распространяется через HTTP. Удалённый сервис получает запрос и создаёт или продолжает свой span сервера.

Одна трассировка может пересекать границы процессов:

span сервера сервиса A
  ↓
span клиента сервиса A
  ↓
span сервера сервиса B
  ↓
span базы данных сервиса B

Это основная ценность распределённой трассировки.

Метрики клиента объясняют здоровье зависимости в агрегате

Исходящий вызов также вносит вклад в агрегированные метрики.

Метрика клиента может ответить:

Как часто сервис рекомендаций даёт сбой?
Выросла ли его p95-задержка с 60 мс до 400 мс?
Сколько произошло таймаутов?

На эти вопросы дорого отвечать только по логам.

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

Телеметрия HTTP-клиента Kora предоставляет согласованное место для этих сигналов.

Трассировка объясняет, какая зависимость доминировала в одном запросе

Метрики могут показывать, что p95 клиента рекомендаций — 700 мс. Но представьте, что один пользователь сообщает о запросе на 2,2 секунды.

Трассировка может показать:

HTTP-СЕРВЕР              2200 мс
└─ dashboard.build       2190 мс
   ├─ PostgreSQL            9 мс
   └─ Recommendation     2160 мс

Теперь проблема немедленно локализована. Репозиторий не медленный. JSON-сериализация не съедает две секунды. Удалённая зависимость доминирует в бюджете задержки.

Это разница между агрегированным обнаружением и причинно-следственной диагностикой.

Логирование клиента должно быть намеренно ограничено

Логирование HTTP-клиента полезно, но опасно.

Запросы могут содержать заголовки авторизации, куки, персональные данные, большие JSON-полезные нагрузки или бинарные тела. Поэтому производственное логирование нуждается в маскировании и ограничениях размера.

Цель — не логировать каждый байт каждой сетевой операции. Цель — логировать достаточно структурированного контекста, чтобы безопасно объяснить поведение.

Полезные поля могут включать:

имя клиента
операцию
метод
маршрут
статус
длительность
тип ошибки
traceId

Заголовки и тела следует логировать только тогда, когда есть ясная причина и соответствующее маскирование.

Наблюдаемость, которая утекает учётные данные, — это не наблюдаемость. Это инцидент.

Репозиторий — ещё одна граница наблюдаемости

Теперь проследим другую ветвь:

UserRepository.findById(userId)

Сгенерированный репозиторий Kora исполняет JDBC-запрос. Эта операция должна быть наблюдаемой независимо от родительского HTTP-запроса.

Трассировка может содержать:

HTTP-СЕРВЕР
GET /users/{id}/dashboard
  ↓
БД-КЛИЕНТ
UserRepository.findById

Запрос также вносит вклад в метрики базы данных.

Это важно, потому что базы данных часто доминируют в производительности бэкенда. Если слой репозитория тёмен, одна из самых важных границ запроса невидима.

Телеметрия базы данных должна описывать операцию, а не только драйвер

Телеметрия базы данных может наблюдать несколько осмысленных фаз:

соединение получено
исполнение оператора начинается
запрос завершается успехом
запрос завершается сбоем
операция заканчивается

Это различие позволяет операторам отделять ожидание соединения от фактического исполнения запроса базой данных.

Это разные проблемы.

Если задержка растёт из-за насыщения пула, добавление индекса может ничего не дать. Если медленно само исполнение, увеличение размера пула может сделать систему хуже.

Хорошая наблюдаемость базы данных помогает различать ожидание ресурса и реальную работу базы данных.

Стабильная идентичность запроса лучше неограниченных SQL-меток

Метрикам нужны ограниченные идентификаторы.

Операция репозитория, такая как:

UserRepository.findById

или стабильный идентификатор запроса хорошо подходит для агрегации.

Полный текст SQL обычно плохая метка метрики, потому что он может быть длинным, динамичным или иметь высокую кардинальность.

Хороший дизайн:

метрики:
стабильная идентичность операции

логи/трассировки:
более богатый контекст запроса, когда нужно

логирование TRACE:
полный SQL там, где безопасно

Это сохраняет дашборды полезными, не превращая каждый чуть отличающийся запрос в отдельный временной ряд.

Логирование SQL должно быть инструментом отладки

Логирование каждого тела SQL на обычных производственных уровнях может создавать большой объём логов, стоимость хранения и риск чувствительных данных.

Лучшая иерархия:

INFO/DEBUG:
идентичность операции, длительность, сбой

TRACE:
полный текст SQL, где уместно

Точная политика зависит от среды и требований безопасности.

Телеметрия базы данных Kora поддерживает более широкий принцип, что структурированные метаданные запроса и подробный SQL не следует смешивать.

Метрики базы данных завершают картину задержки запроса

Предположим, задержка HTTP растёт, и трассировка показывает, что виноват span базы данных.

Метрики базы данных могут ответить, это один аномальный запрос или замедление всего флота.

Например:

p95 db.client.operation.duration растёт
активные соединения пула = максимум
задержка получения соединения растёт

указывает на насыщение.

В то время как:

пул здоров
одна операция запроса медленная

указывает на проблему плана запроса или формы данных.

Наблюдаемость работает, сужая пространство гипотез.

Полная трассировка — это граф исполнения

Для нашего примера запроса трассировка может выглядеть так:

HTTP-СЕРВЕР
GET /users/{id}/dashboard
│
└── БИЗНЕС
    user.dashboard.build
    │
    ├── БД-КЛИЕНТ
    │   UserRepository.findById
    │
    └── HTTP-КЛИЕНТ
        GET /recommendations
        │
        └── спаны нижестоящего сервиса...

Каждый узел отвечает:

Сколько времени занял этот шаг?
Он упал?
Какие атрибуты его описывают?

Дерево отвечает:

Как этот запрос потратил свой бюджет задержки?

Вот почему трассировка — один из лучших инструментов для отладки распределённой задержки.

Бюджетирование задержки становится видимым

Предположим, SLO запроса:

p95 < 300 мс

Трассировка показывает:

HTTP итого               260 мс
накладные расходы бизнеса   10 мс
база данных                20 мс
удалённый HTTP            220 мс
сериализация                5 мс
прочее                     5 мс

Теперь у сервиса осталось только 40 мс бюджета, прежде чем он нарушит SLO.

Наблюдаемость превращает задержку в инженерный бюджет, а не в интуицию.

Без структуры трассировки команды часто оптимизируют не тот слой.

Параллельная работа ясно видна в трассировках

Если независимые операции выполняются параллельно с использованием виртуальных потоков или структурированной конкурентности, трассировка может раскрыть перекрытие.

Например:

HTTP-СЕРВЕР                     180 мс
└─ dashboard.build              170 мс
   ├─ БД user                    60 мс
   └─ HTTP рекомендации         150 мс

Если оба дочерних элемента начинаются почти одновременно, общая задержка ближе к максимуму длительности дочерних элементов, чем к их сумме.

Визуализация трассировки делает это очевидным.

Это ещё одна причина, почему распространение контекста через конкурентные задачи важно: без него параллельная дочерняя работа может оторваться от родительского запроса.

Виртуальные потоки не устраняют необходимость распространения контекста

Ориентированная на виртуальные потоки архитектура Kora 2 упрощает синхронное программирование, но виртуальные потоки не делают контекст наблюдаемости корректным автоматически.

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

Преимущество в том, что синхронные стеки вызовов и семантику «поток на задачу» часто легче понять, чем глубоко асинхронные цепочки колбэков.

Наблюдаемость должна использовать эту простоту, а не заново вводить ручную обвязку контекста.

Структурированные логи — это не просто JSON-логи

Структурированное логирование иногда сводят к «мы выводим JSON». Это упускает суть.

Полезный структурированный лог имеет стабильные семантические поля.

Например:

{
    "level": "WARN",
    "event": "recommendation_fallback",
    "userId": "982374723",
    "reason": "timeout",
    "traceId": "79d65...",
    "spanId": "03fa..."
}

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

Человекочитаемые сообщения могут существовать. Структурированные поля делают событие операционно искомым.

Логи должны фиксировать решения, а не каждый вход в функцию

Распространённый анти-паттерн:

Вход в метод A
Выход из метода A
Вход в метод B
Выход из метода B

Распределённая трассировка уже описывает форму исполнения более эффективно.

Логи должны фиксировать информацию, которую трассировка не может вывести.

Хорошие примеры включают:

выбран запасной вариант
политика риска отклонила транзакцию
запись кеша инвалидирована
удалённый ответ проигнорирован, потому что версия устарела
переход бизнес-состояния отклонён
выбран режим конфигурации

Трассировка говорит вам, куда пошло исполнение.

Лог говорит вам, почему код выбрал этот путь.

Это разделение держит объём логов управляемым.

Корреляция по трассировке делает логи резко более ценными

Без корреляции по трассировке такой лог, как:

Рекомендация истекла по таймауту

может потребовать поиска вокруг временной метки и догадок, какой запрос его породил.

С:

traceId=79d65...

рабочий процесс становится:

  1. открыть медленную трассировку;
  2. запросить логи по идентификатору трассировки;
  3. прочитать решения приложения ровно для этого запроса.

Это одно из самых высокорычажных улучшений, которые сервис может внести в свою модель логирования.

Метрики обычно первый сигнал инцидента

Представьте, что зависимость рекомендаций становится медленной.

Первый признак может быть на дашборде:

GET /users/{id}/dashboard
p95-задержка:
120 мс → 850 мс

или алерт по скорости выгорания SLO.

Метрики идеальны для обнаружения, потому что их дёшево агрегировать непрерывно.

Они отвечают:

Что-то не так?
Насколько это широко?
Когда это началось?

Обычно они не отвечают «почему».

Вот тут вступают трассировки и логи.

Трассировки сужают инцидент

По алерту метрики оператор изучает репрезентативные медленные трассировки.

Несколько показывают:

RecommendationClient = 700 мс
Database = 10 мс

Теперь область инцидента сужается:

входящий сервер здоров
база данных здорова
удалённая зависимость рекомендаций медленная

Сервис может немедленно сосредоточиться на правильной зависимости.

Это наблюдаемость, выполняющая полезную работу, а не просто генерирующая данные.

Логи объясняют реакцию приложения

Та же трассировка может содержать:

event=recommendation_fallback
reason=deadline_exceeded
fallback=empty_recommendations

Теперь оператор знает не только, что удалённая зависимость была медленной, но и как отреагировало приложение.

Возможно, эндпоинт всё равно вернул HTTP 200 с ухудшенными данными. Возможно, вернул 503. Возможно, запасной вариант был отключён для подмножества запросов.

Метрики и спаны не всегда могут объяснить эти решения приложения. Логи могут.

Наблюдаемость наиболее мощна, когда сигналы согласуются

Хорошо инструментированный инцидент может дать:

Метрика

http.server.request.duration p95 = 850 мс

Трассировка

span RecommendationClient = 720 мс

Метрика клиента

http.client.request.duration p95 = 710 мс

Лог

recommendation_fallback reason=timeout

Трассировка нижестоящего сервиса

ожидание пула базы данных = 650 мс

Теперь причинно-следственная цепочка может продолжаться между сервисами.

Каждый сигнал подтверждает и уточняет остальные.

Пробы решают другую проблему

Метрики, трассировки и логи предназначены в первую очередь для людей и систем мониторинга.

Пробы — управляющие сигналы для платформы.

Проба живости спрашивает:

Должен ли этот процесс продолжать существовать?

Проба готовности спрашивает:

Должен ли этот экземпляр прямо сейчас получать трафик?

Путаница этих вопросов может создавать серьёзные производственные инциденты.

Kora раскрывает их как отдельные операционные концепции.

Живость должна быть консервативной

Сбой живости может заставить Kubernetes или другой супервизор перезапустить процесс.

Это мощное действие.

Поэтому живость должна представлять состояние, которое перезапуск процесса правдоподобно может исправить.

Временный сбой базы данных обычно не хороший сбой живости.

Представьте сто реплик и двухсекундное прерывание сети базы данных. Если каждое приложение проваливает живость, потому что база данных временно недоступна, оркестратор может перезапустить весь флот.

Теперь краткая проблема зависимости превратилась в шторм перезапусков.

Управляющие сигналы наблюдаемости могут усиливать сбои, если смоделированы неправильно.

Готовность — о допуске трафика

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

Во время запуска:

граф приложения инициализируется
пул базы данных прогревается
требуемая зависимость недоступна

процесс может быть жив, но не готов.

Во время завершения работы готовность должна в итоге перестать рекламировать экземпляр для нового трафика, пока идёт слив работы в полёте.

Основное правило:

жив
≠
готов

Процесс может быть полностью жив, намеренно не обслуживая трафик.

Пробы принадлежат отдельному системному порту

Операционные эндпоинты обычно не следует выставлять на том же публичном интерфейсе, что и бизнес-трафик.

Чистое развёртывание может использовать:

публичный порт
8080

системный порт
8085

с такими системными эндпоинтами, как:

/metrics
/system/liveness
/system/readiness

Это создаёт полезную границу безопасности и топологии.

Бизнес-клиенты обращаются к публичному API.

Prometheus, kubelet, сайдкары и агенты мониторинга обращаются к системному API.

Это разделение следует проектировать с самого начала, а не латать позже в правилах ingress.

Системный порт — часть производственной архитектуры

Диаграмма сервиса не должна показывать только:

Клиент → 8080

Она также должна показывать:

Prometheus → 8085 /metrics
Kubernetes → 8085 /system/readiness
Kubernetes → 8085 /system/liveness

Это реальные пути производственного трафика.

Если сетевая политика или конфигурация сервисной сетки блокирует их, приложение может быть функционально корректным, но операционно сломанным.

Операционные эндпоинты — это архитектура развёртывания.

Готовность — часть производительности запуска

Kora подчёркивает быстрый запуск, но операционно значимая метрика — не:

main() запущен

А:

экземпляр готов принимать трафик

JVM может загрузиться быстро и всё равно потратить двадцать секунд в ожидании инициализации до готовности.

С точки зрения платформы это двадцатисекундный запуск.

Вот почему готовность принадлежит бенчмаркам запуска и SLO развёртывания.

Готовность также часть автоскейлинга

Предположим, горизонтальный автоскейлер добавляет десять Pod во время всплеска.

Эти Pod дают полезную ёмкость только после того, как становятся готовыми.

Если готовность приходит быстро, новые экземпляры могут поглотить всплеск.

Если готовность занимает десятки секунд, автоскейлер реагирует слишком медленно, и существующие экземпляры остаются перегруженными.

Здесь наблюдаемость не просто диагностика.

Сигнал готовности напрямую участвует в управлении ёмкостью.

Корректное завершение работы завершает жизненный цикл

Правильный запуск — лишь половина производственного поведения жизненного цикла.

Сервисам также нужно правильно останавливаться.

Опасная последовательность завершения:

сигнал завершения
  ↓
немедленно убить сервер
  ↓
бросить запросы в полёте

Более безопасная последовательность:

завершение начинается
  ↓
перестать принимать новую работу / стать неготовым
  ↓
позволить работе в полёте завершиться
  ↓
закрыть ресурсы
  ↓
выход

HTTP-сервер Kora включает поведение корректного завершения с настраиваемым периодом ожидания.

Это операционная функция и функция доступности.

Корректное завершение должно сохранять завершение трассировки

Представьте, что запрос выполняется, когда начинается развёртывание.

Сервис уже создал:

span HTTP-сервера
span базы данных
span удалённого клиента

Если процесс умирает внезапно, телеметрия может быть неполной. Клиент может получить сброс. Span сервера может никогда не экспортироваться. Транзакция может быть прервана.

Корректный слив увеличивает шанс, что запрос завершится, спаны закроются, логи сбросятся, а экспортёры телеметрии отправят буферизованные данные до выхода процесса.

Наблюдаемость и поведение завершения усиливают друг друга.

Экспортёрам тоже нужна семантика завершения

Экспортёры трассировки часто буферизуют спаны перед отправкой. Аппендеры логирования могут буферизовать. Другие компоненты телеметрии могут владеть ресурсами.

Корректный жизненный цикл приложения должен давать этим компонентам возможность сброситься и чисто остановиться.

Это ещё одна причина, почему наблюдаемость должна жить в графе приложения и жизненном цикле, а не в ad hoc статической инициализации.

Если инфраструктура — реальный компонент, фреймворк может управлять порядком запуска и завершения.

Жизненный цикл графа приложения помогает эксплуатации

Модель графа Kora операционно полезна, потому что у компонентов есть явные зависимости и жизненный цикл.

Упрощённый граф может выглядеть так:

HTTP-сервер
  ↓
контроллеры
  ↓
сервисы
  ↓
репозитории / клиенты
  ↓
ресурсы базы данных / сети

телеметрия
  ↓
MeterRegistry / Tracer / логирование

Инициализация и завершение могут следовать известным отношениям зависимостей.

Инфраструктуре не нужно гадать, какой рантайм-синглтон должен остановиться первым.

Наблюдаемость должна быть модульной, но согласованной

Модульная система Kora позволяет сервисам включать только то, что им нужно.

Это полезно, потому что не у каждого приложения ровно одни и те же интеграции.

Но модульность не должна производить несовместимые конвенции телеметрии.

JDBC, Kafka, HTTP, gRPC, планировщик и другие интеграции всё равно должны разделять общие операционные идеи:

логирование
метрики
трассировку
контекст
идентичность операции
длительность
ошибку

Эта согласованность снижает количество кастомных обёрток, которые каждой команде приходится изобретать.

Одна модель телеметрии во всех модулях снижает фрагментацию платформы

Представьте компанию с двумястами сервисами.

Если каждая команда реализует HTTP-метрики по-разному, метрики базы данных по-разному и трассировку Kafka по-разному, платформенная команда не может строить переиспользуемые дашборды или runbook'и.

Конвенции уровня фреймворка позволяют общие операционные представления:

частота запросов
частота ошибок
задержка зависимостей
длительность базы данных
обработка потребителем

Это организационное преимущество, гораздо большее, чем удобство любого отдельного API метрик.

Micrometer и OpenTelemetry образуют прагматичный стек

Kora использует Micrometer для метрик и OpenTelemetry для трассировки и семантических конвенций.

Это прагматичная JVM-комбинация.

Micrometer глубоко устоялся для метрик, ориентированных на Prometheus. OpenTelemetry предоставляет вендор-нейтральную модель трассировки и общий семантический язык.

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

Это сохраняет бэкенд наблюдаемости заменяемым.

Наблюдаемость не должна привязывать сервис к одному вендору

Сервис должен уметь отправлять трассировки в Jaeger, Tempo, управляемого вендора наблюдаемости или другой совместимый с OpenTelemetry бэкенд без переписывания бизнес-кода.

Аналогично, метрики в стиле Prometheus может потреблять большая экосистема.

Вендор-нейтральная инструментация снижает стоимость смены бэкенда позже.

Фреймворк должен владеть семантикой инструментации, а не привязкой к вендору.

Имена метрик и семантические конвенции важны

Метрика полезна не просто потому, что существует.

Имена и измерения нуждаются в стабильном значении.

Например:

http.server.request.duration

гораздо более переиспользуема, чем:

my_http_timer_2

Общие семантические конвенции позволяют дашбордам и алертам работать между сервисами и снижают бремя документации.

Согласованность — множитель силы.

Бизнес-метрики требуют ещё большей дисциплины

Метрики фреймворка поставляются с определённой семантикой.

Бизнес-метрики — нет.

Предположим, приложение создаёт:

orders.created

Команде нужно точно определить, когда это инкрементируется.

До фиксации транзакции?

После фиксации?

При повторе?

Считается ли идемпотентное воспроизведение снова?

Без точного определения метрика может выглядеть авторитетной, измеряя при этом не то.

Бизнес-телеметрию следует рассматривать как API.

Избегайте подсчёта одного бизнес-события на нескольких слоях

Распространённая ошибка — инкрементировать:

order.created

в контроллере, сервисе и издателе событий.

Теперь одно семантическое событие может считаться несколько раз.

Инфраструктурные метрики принадлежат инфраструктурным границам.

Бизнес-метрики должны жить в семантической точке, где событие становится истинным.

Для создания заказа это может быть после успешной фиксации транзакции.

Точное место зависит от предметной области.

Логам тоже нужны стабильные имена событий

Структурированный лог легче запрашивать, когда он содержит:

event=order_creation_failed

чем когда сообщение варьируется между:

Не удалось создать заказ
Ошибка создания заказа
Сбой при создании заказа

Человекочитаемые сообщения могут меняться.

Стабильные поля событий дают машиночитаемую семантику.

Это делает поиск по инцидентам и алертинг более надёжными.

Сэмплирование трассировок меняет то, что трассировки могут вам сказать

Метрики обычно спроектированы представлять каждую операцию.

Трассировки могут сэмплироваться.

Это значит, что трассировка не должна быть агрегированным источником истины для таких вопросов, как:

Сколько запросов упало?

Если сохраняется лишь доля трассировок, подсчёт ошибок в трассировках вводит в заблуждение.

Метрики остаются агрегированной истиной.

Трассировка даёт подробные примеры и причинно-следственные пути.

Сигналы дополняют друг друга.

Трассировки ошибок могут заслуживать другого сэмплирования

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

Работа фреймворка — испускать корректные данные трассировки.

Политика сэмплирования принадлежит платформе.

Для систем с высоким объёмом сэмплирование имеет прямой эффект на стоимость.

Логи с высокой кардинальностью тоже стоят денег

Перемещение идентификатора пользователя из тегов метрик в логи не делает его бесплатным.

Логи всё равно потребляют хранилище и индексацию.

Разница в том, что системы логов спроектированы для записей уровня событий, в то время как системы метрик оптимизированы вокруг ограниченных измерений временных рядов.

Команды всё равно должны избегать логирования идентификаторов, не дающих операционной ценности.

Наблюдаемость должна быть намеренной.

PII и секреты требуют явной политики

Данные наблюдаемости часто покидают приложение и попадают в централизованные системы.

Это значит, что трассировки и логи могут стать вторичными хранилищами данных.

Такие заголовки, как:

Authorization
Cookie
Set-Cookie
ключи API

должны маскироваться.

Тела могут содержать персональные, финансовые данные или учётные данные.

Атрибуты спанов тоже требуют ревью.

Конвейер наблюдаемости заслуживает той же дисциплины безопасности, что и любая другая система данных.

Уровни логирования должны иметь операционное значение

Полезная конвенция может быть:

ERROR
неожиданный сбой операции, требующий внимания

WARN
ухудшенное поведение или устранимая аномалия

INFO
значимое событие жизненного цикла или бизнеса

DEBUG
диагностический контекст, обычно отключённый

TRACE
очень подробная информация протокола/запроса

Если каждый рутинный запрос логируется на INFO, значимые информационные события тонут в шуме.

Kora может предоставить модульное логирование. Командам всё равно нужно определить, что уровни означают операционно.

Логи HTTP-запросов не должны заменять HTTP-метрики

Можно вычислить частоту запросов, логируя каждый запрос и агрегируя логи.

Обычно это плохая замена метрике.

Метрики спроектированы для агрегированной частоты, ошибок и распределений задержки.

Используйте логи, когда реально нужны записи отдельных запросов.

Используйте метрики для агрегированного здоровья.

Тот же принцип применим к операциям базы данных.

Пробы не должны становиться мониторингом зависимостей

Проба готовности может включать выбранное состояние зависимостей, когда обслуживание трафика без этой зависимости невозможно.

Но пробы не должны становиться полной системой мониторинга.

Если запрос готовности синхронно проверяет десять нижестоящих сервисов, сама проба становится хрупкой и медленной.

Мониторинг зависимостей принадлежит в первую очередь метрикам, трассировке, состоянию отказоустойчивости и алертам.

Дизайн проб должен оставаться сфокусированным на здоровье процесса и допуске трафика.

Готовность может явно представлять прогрев

У некоторых приложений есть легитимный прогрев:

загрузка конфигурации
инициализация пула соединений
предзагрузка важных данных
построение локальных индексов

В это время:

живость = здорова
готовность = false

Как только приложение может выполнять свой контракт обслуживания:

готовность = true

Это гораздо лучше произвольных задержек запуска.

Приложение говорит платформе правду о своём состоянии.

Ложная готовность хуже медленной готовности

Сервис, сообщающий о готовности до того, как может обрабатывать реальный трафик, может получать запросы, пока критические ресурсы всё ещё непригодны.

Первые пользователи после развёртывания платят стоимость инициализации или получают ошибки.

Быстрая готовность ценна только если она правдива.

Быстрая архитектура запуска Kora должна сочетаться с реалистичной семантикой готовности.

Пробам нужен собственный SLO

Операционные эндпоинты должны быть быстрыми.

Если сама готовность занимает три секунды, потому что выполняет тяжёлую диагностику, оркестратор не может принимать быстрые решения.

Эндпоинты проб обычно должны возвращать состояние, которое приложение уже поддерживает, а не выполнять дорогую синхронную работу при каждом вызове.

Это сохраняет оркестрацию отзывчивой.

У корректного завершения есть бюджет

HTTP-сервер Kora может ждать обработки в полёте во время завершения.

Это ожидание не может быть бесконечным.

У платформы тоже есть период отсрочки завершения.

Жизненному циклу нужен бюджет:

время до становления неготовым
+
распространение в балансировщике нагрузки
+
слив работы в полёте
+
завершение ресурсов
<
период отсрочки завершения платформы

Если приложение ждёт дольше, чем позволяет оркестратор, его всё равно могут убить.

Корректное завершение должно проектироваться под среду развёртывания.

Длинные запросы усложняют корректное завершение

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

Ни один фреймворк не может магически гарантировать, что каждый запрос завершится во время раскатки.

Сервису нужна явная стратегия:

  • сделать операцию идемпотентной;
  • увеличить период отсрочки;
  • перенести длинную работу в систему заданий;
  • чекпоинтить прогресс;
  • перестать принимать длинную работу перед завершением.

Корректное завершение — это архитектура, а не просто настройка HTTP-сервера.

Планируемым заданиям и потребителям тоже нужна семантика завершения

Та же задача жизненного цикла существует за пределами HTTP.

У планировщика могут быть выполняющиеся задания.

Потребитель Kafka может обрабатывать запись.

Операция базы данных может быть в полёте.

Каждому модулю нужно определить, ждёт ли он текущую работу, прерывает ли её, фиксирует ли прогресс или повторяет где-то ещё.

Общая модель жизненного цикла делает завершение связным во всём приложении.

Наблюдаемость обмена сообщениями следует тому же паттерну

Статья сосредоточена на HTTP-запросе, но та же архитектура применима к Kafka или другому обмену сообщениями.

У операции потребителя есть:

получение сообщения
обработка
исходящие вызовы
работа с базой данных
ack / фиксация

Ей нужны метрики длительности, метрики ошибок, трассировка/контекст, где уместно, структурированные логи и поведение жизненного цикла.

Согласованность телеметрии во всём фреймворке означает, что разработчикам не нужна новая философия наблюдаемости для каждой интеграции.

gRPC — та же история на другой границе протокола

Вызов gRPC-сервера — ещё одна входящая операция.

gRPC-клиент — ещё одна исходящая зависимость.

Протокол отличается, но паттерн наблюдаемости нет:

входящий span сервера
  ↓
бизнес-работа
  ↓
исходящий span клиента
  ↓
span репозитория

Эта общность важна для платформ, поддерживающих смешанные протоколы.

Наблюдаемость должна следовать реальным границам технологий

Философия тонких абстракций Kora полезна здесь.

Операторы уже понимают:

HTTP
JDBC
Kafka
gRPC

Телеметрия должна описывать эти технологии напрямую.

Запрос к базе данных должен выглядеть как запрос к базе данных.

Вызов HTTP-клиента должен выглядеть как вызов HTTP-клиента.

Потребитель Kafka должен выглядеть как обработка сообщений.

Это позволяет инженерам рассуждать, используя переносимые знания технологий, а не специфичную для фреймворка терминологию.

Фреймворк должен инструментировать там, где у него лучший контекст

Качество инструментации наивысшее вблизи операции.

HTTP-сервер знает шаблон маршрута.

Сгенерированный HTTP-клиент знает метод клиента и маршрут.

Репозиторий знает идентификатор запроса.

Планировщик знает идентичность задания.

Интеграция Kafka знает операцию потребителя.

Это позволяет Kora испускать стабильные имена операций, не заставляя разработчиков приложения вручную их восстанавливать.

Это один из самых сильных аргументов за наблюдаемость уровня фреймворка.

Кастомные компоненты телеметрии — запасной люк

Телеметрия по умолчанию должна быть полезной для большинства сервисов.

Организациям всё равно могут понадобиться кастомные теги метрик, специальные поля структурированных логов, кастомные имена операций, проприетарные данные корреляции или политика маскирования на уровне организации.

Модель графа приложения Kora делает компоненты телеметрии заменяемыми или настраиваемыми.

Это следует тому же паттерну, что и остальной фреймворк: сильные значения по умолчанию, явные компоненты, заменяемая реализация.

Кастомизация должна улучшать согласованность, а не фрагментировать её

Если каждый сервис заменяет логгер базы данных по-разному, платформа теряет ценность стандартизации.

Кастомизация наиболее ценна, когда она обеспечивает правила уровня компании:

метаданные сервиса
среда
регион
команда
именование операций
политика маскирования

Точки расширения следует использовать централизованно, где возможно.

Идентичность сервиса должна быть корректной

Бэкендам трассировки нужно знать, какой сервис произвёл span.

Платформам метрик нужна стабильная идентичность сервиса.

Логам нужна идентичность источника.

Операционные метаданные обычно включают:

service.name
service.version
environment
region / datacenter
идентичность экземпляра

Точная схема зависит от конвенций платформы.

Если идентичность сервиса несогласованна, межсервисная наблюдаемость становится болезненной.

Метаданные версии необходимы во время раскаток

Предположим, частота ошибок растёт во время развёртывания.

Если телеметрия несёт версию приложения, операторы могут сравнить:

версия A
частота ошибок 0,1%

версия B
частота ошибок 7%

Теперь уверенность в откате высока.

Без измерений версии регрессия выглядит как общая проблема сервиса.

Наблюдаемость должна быстро отвечать:

Вызвало ли это развёртывание инцидент?

Идентичность экземпляра полезна, но дорога

Диагностика на уровне экземпляра иногда важна:

один Pod имеет аномальную задержку

Но идентификаторы экземпляров создают измерения с высокой кардинальностью.

Они могут принадлежать логам, трассировкам или выбранным инфраструктурным метрикам, а не каждой метрике приложения.

Кардинальность следует рассматривать как бюджет.

Стоимость наблюдаемости — реальная производственная стоимость

Сервис может тратить значительные деньги на:

  • приём логов;
  • хранение трассировок;
  • кардинальность метрик;
  • сетевой экспорт;
  • вычисления бэкенда.

«Больше телеметрии» не автоматически лучше.

Цель:

максимум операционной информации
на единицу стоимости телеметрии

Стандартизированная фреймворком телеметрия помогает, потому что даёт высокоценные сигналы без того, чтобы каждая команда независимо переинструментировала.

Метрикам нужны бюджеты кардинальности

У платформы должны быть явные правила.

Не тегируйте метрики небрежно:

userId
requestId
orderId
sessionId
сырой URL
сообщение исключения

Более безопасные измерения включают:

шаблон маршрута
HTTP-метод
статус
имя операции
имя клиента
систему базы данных
ограниченный тип ошибки

Метрика с неограниченными метками — производственный риск.

Атрибуты трассировки тоже требуют бюджетов полезной нагрузки

Бэкенды трассировки также имеют лимиты и стоимость.

Span с сотнями атрибутов или большими телами запросов дорог.

Прикрепляйте поля, которые объясняют или фильтруют операцию.

Не дублируйте полную полезную нагрузку приложения в телеметрию.

То же правило применимо к логам.

Наблюдаемость должна сохранять смысл, а не воспроизводить каждый байт бизнес-данных.

Метрики, логи и трассировки должны разделять словарь

Предположим, исходящая операция:

RecommendationClient.getRecommendations

Полезно, если сигналы согласованно описывают одну и ту же концептуальную операцию.

Например:

span:
GET /recommendations

метрика:
client=RecommendationClient
route=/recommendations

лог:
operation=getRecommendations

Схемы не обязаны быть идентичными.

Они должны быть взаимно понятными.

Классификация ошибок должна быть согласованной

Один модуль может сообщать:

timeout

другой:

SocketTimeoutException

а лог:

deadline_exceeded

Они могут описывать один и тот же операционный сбой.

Метрики выигрывают от ограниченных категорий ошибок.

Трассировки и логи могут сохранять подробную информацию об исключениях.

Платформа должна нормализовать словарь ошибок там, где полезно, не выбрасывая богатые диагностические детали.

Исключения не должны становиться тегами метрик напрямую

Сырые сообщения исключений нестабильны и часто имеют высокую кардинальность.

Метрики обычно должны использовать ограниченные категории ошибок.

Трассировки и логи — лучшие места для стектрейсов и подробной информации об исключениях.

Это ещё один пример того, как давать каждому сигналу тот тип информации, который он обрабатывает лучше всего.

RED хорошо подходит HTTP-сервисам

Базовый дашборд сервиса может начинаться с RED:

Rate (частота)
Errors (ошибки)
Duration (длительность)

Для каждого важного маршрута:

запросов/сек
частота ошибок
гистограмма задержки

Это отвечает на большинство вопросов первого уровня о здоровье сервиса.

Затем дашборды ресурсов добавляют сигналы насыщения, такие как CPU, куча, пулы баз данных и файловые дескрипторы.

Стандартная телеметрия Kora даёт большую часть сырья.

USE подходит ресурсам

Для таких ресурсов, как пулы соединений и CPU, полезен USE:

Utilization (использование)
Saturation (насыщение)
Errors (ошибки)

Для пула JDBC:

использование:
активные соединения / максимум

насыщение:
ожидающие / задержка получения

ошибки:
сбои соединений / запросов

Сочетание RED для операций сервиса с USE для дефицитных ресурсов даёт операторам дисциплинированную модель расследования.

Хороший дашборд отражает путь запроса

Для нашего примера сервиса полезная иерархия дашборда выглядит так.

Входящие:

HTTP RPS
частота ошибок HTTP
HTTP p50/p95/p99
активные запросы

Бизнес:

построения дашбордов
использование запасных вариантов
бизнес-сбои

Исходящий HTTP:

RPS RecommendationClient
распределение статусов
таймауты
задержка

База данных:

длительность запроса
использование пула соединений
задержка получения
ошибки запросов

Рантайм:

CPU
куча
GC
потоки / поведение носителей, где релевантно
время работы
файловые дескрипторы

Дашборд следует тому же мысленному пути, что и код.

Это делает навигацию по инцидентам интуитивной.

Алертинг должен начинаться с влияния на пользователя

Не каждая метрика заслуживает алерта.

Полезные условия алерта часто включают:

скорость выгорания SLO
устойчивую частоту ошибок
сбой готовности
насыщение пула с влиянием на задержку
частоту таймаутов зависимостей
истощение ресурсов

Краткий всплеск CPU может не требовать действий, если задержка и частота ошибок остаются здоровыми.

Алертите на симптомы, важные для пользователей, затем используйте внутренние метрики для диагностики.

Трассировки особенно ценны после алерта

Сильный рабочий процесс инцидента:

алерт
  ↓
дашборд метрик
  ↓
репрезентативная трассировка
  ↓
логи для трассировки
  ↓
дашборд зависимостей / ресурсов

Каждый шаг сужает проблему.

Модульная телеметрия Kora поддерживает этот рабочий процесс, потому что основные границы запроса уже инструментированы.

Логи должны быть искомыми по идентификатору трассировки

Если логи включают traceId и spanId как структурированные поля, UI наблюдаемости может связывать трассировку и её логи.

Вместо догадок вокруг временной метки оператор ищет:

traceId=79d65...

и получает ровно релевантные события.

Это одна из самых ценных интеграций между трассировкой и логированием.

Не конкатенируйте идентификаторы трассировки вручную повсюду

Код приложения не должен снова и снова делать:

log.info("traceId={} ...", traceId);
log.info("traceId={} ...", traceId)

Интеграция логирования должна обогащать записи из текущего контекста автоматически.

Бизнес-код должен логировать бизнес-поля и события.

Фреймворк должен предоставлять идентичность корреляции.

Это разделение держит логирование согласованным.

Наблюдаемость должна переживать пути сбоев

Инструментацию легче всего реализовать на пути успеха.

Её ценность наивысшая на пути сбоя.

Каждое наблюдение нуждается в ясном пути завершения, когда:

  • обработчик бросает исключение;
  • HTTP-клиент истекает по таймауту;
  • запрос к базе данных падает;
  • происходит отмена;
  • выполняется запасной вариант.

Спаны должны завершаться.

Ошибки должны фиксироваться.

Метрики должны отражать сбой.

Логи должны сохранять контекст.

Абстракции наблюдения уровня фреймворка помогают централизовать эту корректность.

Всегда закрывать наблюдение — скрытое правило корректности

Концептуально инструментация делает:

observation.start()
try
    operation()
catch
    observation.error(...)
finally
    observation.end()

Если наблюдение не завершается при исключении, метрики и спаны становятся вводящими в заблуждение.

Это ровно тот вид повторяющегося кода корректности, который принадлежит телеметрии фреймворка, а не каждому бизнес-методу.

Отключённая телеметрия должна становиться дешёвой

Иногда сигнал намеренно отключается для модуля.

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

Модули Kora могут использовать no-op пути телеметрии, когда сигналы отключены.

Это важно, потому что конфигурация наблюдаемости должна иметь предсказуемую стоимость времени выполнения.

Но отключение всего обычно ложная экономия

Сервис может сэкономить немного CPU, отключив наблюдаемость, а затем потерять часы во время следующего инцидента.

Лучший паттерн:

метрики всегда включены
базовая трассировка включена с разумным сэмплированием
структурированные логи INFO/WARN
логирование тел выключено
очень подробное логирование SQL выключено

Держите высокоценные низкостоимостные сигналы доступными. Контролируйте дорогие детали конфигурацией и сэмплированием.

Производственные значения по умолчанию должны отдавать предпочтение безопасной видимости

Значения по умолчанию фреймворка важны, потому что многие сервисы не настраивают каждую опцию.

Безопасные значения по умолчанию должны избегать:

  • утечки секретов;
  • неограниченных меток метрик;
  • логирования огромных полезных нагрузок;
  • чрезмерных атрибутов трассировки.

В то же время они должны давать достаточно информации для эксплуатации сервиса.

Наблюдаемость должна быть безопасной по умолчанию, а не отсутствующей по умолчанию.

Локальная разработка должна использовать те же сигналы

Наблюдаемости легче доверять, когда разработчики используют её до продакшена.

Локальная среда может запускать:

сервис
метрики в стиле Prometheus
Jaeger / Tempo / collector
структурированные логи

Тогда разработчик может отправить один запрос и изучить его трассировку, метрики, логи и пробы.

Это делает телеметрию частью обычной разработки, а не аварийной инфраструктурой, которую никто не понимает до отключения.

Компонентные тесты могут проверять операционный контракт

Тесты могут проверять, что:

эндпоинт метрик существует
готовность ведёт себя корректно
бизнес-метрика инкрементируется
логи несут контекст трассировки
ручной span вложен под span HTTP

Эти тесты не тестируют сам Micrometer или OpenTelemetry.

Они тестируют операционный контракт приложения.

Если обновление платформы случайно удаляет требуемую пробу или метрику, CI должен это поймать.

Тесты чёрного ящика должны включать системный порт

Большинство тестов чёрного ящика сосредоточены на публичных бизнес-API.

Продакшен-ориентированный набор должен также проверять базовое поведение:

эндпоинта метрик
готовности
живости

Это гарантирует, что операционные пути не ломаются в развёртываниях.

Тесты проб должны проверять переходы состояний

Тест готовности, проверяющий только «возвращает 200 после запуска», слаб.

Более полезные тесты проверяют переходы жизненного цикла, где это практично:

до готовности зависимости
→ не готов

после инициализации
→ готов

во время завершения
→ больше не принимает новую работу

Операционная корректность — о переходах состояний, а не о статических эндпоинтах.

Конфигурация наблюдаемости — производственный код

Конфигурация телеметрии включает:

включение метрик
корзины SLO
экспортёр трассировки
идентичность сервиса
уровни логирования
маскирование
пути проб
таймаут завершения

Эти настройки следует версионировать и рецензировать.

Изменение меток метрик или политики сэмплирования может быть операционно так же важно, как изменение Java-кода.

Дашборды и алерты тоже следует версионировать

Телеметрия полезна, только если её потребители остаются в синхронизации.

Зрелые платформы версионируют:

  • дашборды;
  • правила алертов;
  • определения SLO;

вместе с конфигурацией сервиса или платформы.

Иначе сервис может переименовать метрику и молча сломать операционную видимость.

Телеметрия — внутренний API

Метрики, имена спанов, поля структурированных логов и поведение проб — внутренние операционные API.

Дашборды зависят от них.

Алерты запрашивают их.

Runbook'и ссылаются на них.

Небрежное изменение может сломать операции.

Дисциплина именования важна, потому что другие системы потребляют телеметрию.

Один запрос должен быть трассируемым от начала до конца

Вернёмся к исходному запросу:

Клиент
  ↓
HTTP-сервер
  ↓
DashboardService
  ├── UserRepository
  │      ↓
  │   PostgreSQL
  │
  └── RecommendationClient
         ↓
      Сервис рекомендаций

Идеальная трассировка может выглядеть так:

Трассировка 79d65...

SERVER  GET /users/{id}/dashboard           184 мс
  │
  └─ INTERNAL user.dashboard.build          176 мс
       │
       ├─ CLIENT DB UserRepository.findById   12 мс
       │
       └─ CLIENT HTTP GET /recommendations   151 мс
            │
            └─ SERVER нижестоящий ...         145 мс

Соответствующие метрики говорят:

p95 дашборда = 190 мс
p95 запроса к БД = 14 мс
p95 рекомендаций = 155 мс
частота ошибок = 0,3%

Коррелированные логи могут говорить:

traceId=79d65...
event=recommendation_cache_miss

или:

traceId=79d65...
event=recommendation_fallback
reason=timeout

Тем временем системный порт сообщает:

liveness = здоров
readiness = готов

Это наблюдаемость по проектированию.

Что происходит во время инцидента

Предположим, в 13:40 p95 эндпоинта растёт со 180 мс до 1,4 секунды.

Метрики обнаруживают симптом:

http.server.request.duration
route=/users/{id}/dashboard
p95=1.4s

Репрезентативные трассировки локализуют его:

RecommendationClient = 1.25s
Database = 10ms

Метрики клиента подтверждают, что зависимость медленная во многих запросах:

http.client.request.duration
client=RecommendationClient
p95=1.2s

Логи показывают реакцию приложения:

event=recommendation_fallback
частота растёт

Трассировки нижестоящего сервиса раскрывают:

получение из пула базы данных = 900ms

а метрики ресурсов нижестоящего сервиса подтверждают:

активные соединения пула = максимум
ожидающие пула растут
CPU базы данных нормальный

Вероятная проблема теперь — насыщенный пул соединений нижестоящего сервиса, а не исходный HTTP-сервер.

Без коррелированной наблюдаемости тот же инцидент может превратиться в часы предположений.

Хорошая наблюдаемость снижает среднее время до понимания

Команды часто измеряют MTTR, среднее время до восстановления.

Восстановление сильно зависит от среднего времени до понимания.

Сколько времени нужно, чтобы сформировать корректное объяснение сбоя?

Метрики снижают время обнаружения.

Трассировки снижают время локализации.

Логи снижают время семантической интерпретации.

Пробы снижают неоднозначность оркестрации.

Корректное поведение жизненного цикла предотвращает создание развёртываниями дополнительного шума.

Наблюдаемость улучшает надёжность отчасти потому, что сокращает путь от симптома к объяснению.

Наблюдаемость улучшает и инженерию производительности

Те же инструменты, что используются во время инцидентов, ценны для работы с производительностью.

Инженер по производительности может спросить:

Где p99 проводит время?

и изучить трассировки.

Он может коррелировать CPU, GC, задержку базы данных, задержку удалённого клиента и распределения запросов.

Он может сравнивать поведение до и после развёртывания.

Это гораздо информативнее синтетического числа пропускной способности самого по себе.

Наблюдаемость превращает продакшен в источник свидетельств производительности.

Планирование ёмкости требует метрик

Сколько реплик нужно сервису?

Сколько соединений базы данных?

Сколько CPU?

Без метрик команды гадают.

С метриками они могут оценить пиковую частоту запросов, CPU на запрос, активную конкурентность, конкурентность базы данных, насыщение пула и задержку под нагрузкой.

Автоскейлинг и запросы ресурсов могут основываться на измеренном поведении.

Поэтому операционный стек напрямую связан со стоимостью инфраструктуры.

Корректное завершение защищает бюджеты ошибок во время развёртываний

Раскатка может создавать сбои, даже когда новая версия здорова.

Если старые экземпляры завершаются слишком резко, запросы могут сбрасываться при каждом развёртывании.

В масштабе флота это может непрерывно расходовать бюджет ошибок.

Корректное завершение превращает развёртывание из:

убить ёмкость

в:

слить ёмкость

Это функция доступности.

Быстрая готовность и корректное завершение — две стороны одной раскатки

Во время раскатывающейся замены:

новый экземпляр
→ быстро стать готовым

старый экземпляр
→ стать неготовым и безопасно слиться

Контроллер развёртывания зависит от обоих.

Фреймворк, сосредоточенный только на скорости запуска, но не на корректном завершении, оптимизирует половину перехода.

Производственная модель Kora сильнее, потому что готовность, жизненный цикл и корректная остановка принадлежат одной операционной истории.

Наблюдаемость должна объяснять развёртывания

Во время раскатки дашборды должны раскрывать:

трафик старой версии уменьшается
трафик новой версии увеличивается
задержка новой версии
ошибки новой версии
сбои готовности
аномалии завершения

Телеметрия с учётом версии делает валидацию развёртывания измеримой.

Холодные экземпляры нуждаются в немедленной телеметрии

Только что запущенный экземпляр наиболее интересен именно тогда, когда он меньше всего прогрет.

Если наблюдаемость появляется только после долгой инициализации, операторы теряют видимость проблем запуска.

Системный порт и телеметрия жизненного цикла должны становиться пригодными достаточно рано, чтобы объяснить инициализацию и готовность.

Это важно для автоскейлинга, циклов падений, холодных стартов и замены spot-экземпляров.

Логи запуска должны быть структурированы вокруг жизненного цикла

Полезные события жизненного цикла включают:

граф приложения инициализирован
системный HTTP-сервер запущен
публичный HTTP-сервер запущен
пул базы данных инициализирован
readiness=true

а сбои должны идентифицировать, какой компонент упал.

Явный граф приложения Kora даёт логам жизненного цикла реальную структурную основу.

Логи завершения должны зеркалить запуск

Завершение тоже должно быть понятным.

Полезные события могут включать:

завершение инициировано
готовность отключена
слив HTTP начат
завершение ресурсов
сброс телеметрии
завершение завершено

Если Pod стабильно превышает отсрочку завершения, телеметрия должна делать причину видимой.

Наблюдаемость — часть отказоустойчивости

Повторы, circuit breaker'ы, запасные варианты и таймауты нуждаются в телеметрии.

Circuit breaker без метрик опасен, потому что операторы не могут видеть его состояние или частоту отклонений.

Повтор без телеметрии может молча усиливать нагрузку на нижестоящую систему.

Запасной вариант без логов или метрик может скрывать постоянный сбой зависимости.

Успешный HTTP 200 может представлять ухудшенную операцию.

Наблюдаемость должна раскрывать поведение отказоустойчивости, а не только поведение транспорта.

Частота запасных вариантов может быть важнее частоты ошибок

Предположим, сбои рекомендаций скрыты запасным вариантом.

API всё равно возвращает 200.

Частота ошибок HTTP остаётся нулевой.

Но продукт ухудшен.

Бизнес- или отказоустойчивая метрика, такая как:

recommendation.fallback.count

может раскрыть проблему.

Вот почему инфраструктурная телеметрия фреймворка необходима, но недостаточна.

Приложение должно раскрывать значимые состояния деградации.

Попытки повтора нуждаются в видимости

Если зависимость обычно успешна с первой попытки, но начинает требовать двух-трёх повторов, задержка пользователя растёт до появления явных сбоев.

Метрики попыток повтора и исчерпания повторов могут дать раннее предупреждение.

Трассировка может показывать повторные дочерние вызовы.

Логи могут объяснить, почему произошёл повтор.

Политика отказоустойчивости должна быть наблюдаемой, а не молча скрывать сбой.

Отклонение circuit breaker'а отличается от удалённого сбоя

Запрос, отклонённый, потому что breaker открыт, не достиг нижестоящего сервиса.

Это операционно отличается от запроса, который достиг сервиса и упал.

Метрики и трассировки должны сохранять это различие.

Разные причины требуют разных мер.

Таймаутам нужна собственная классификация

Таймаут — это сбой бюджета задержки.

Он часто указывает на медленность зависимости или слишком строгий таймаут.

Метрики должны классифицировать таймауты с ограниченными измерениями.

Трассировки и логи могут содержать более богатые данные исключений/контекста.

Это поддерживает и алертинг, и диагностику.

Наблюдаемость должна существовать до нагрузочного тестирования

Нагрузочный тест без внутренней телеметрии говорит вам только пропускную способность, задержку и ошибки снаружи.

С наблюдаемостью вы также видите CPU, GC, давление пула базы данных, задержку запросов, задержку клиента, усиление повторов и поведение потоков.

Это говорит вам, почему бенчмарк ведёт себя так.

Поэтому телеметрия фреймворка улучшает тестирование производительности, а не только отладку продакшена.

Поведению виртуальных потоков тоже нужна телеметрия

Для Kora 2 поведение виртуальных потоков — важный операционный предмет.

Во время всплесков нагрузки полезные сигналы включают конкурентность запросов, CPU, насыщение пула базы данных, задержку и поведение носителей/потоков, где доступно.

Если задержка растёт, а ожидание пула увеличивается, бутылочное горлышко — конкурентность базы данных.

Если CPU насыщается, добавление виртуальных потоков не помогает.

Наблюдаемость предотвращает расплывчатые диагнозы вроде «виртуальные потоки медленные», когда дефицитный ресурс находится в другом месте.

Телеметрия должна вести вас к дефицитному ресурсу

Каждая система в итоге упирается во что-то:

CPU
соединения базы данных
блокировки базы данных
конкурентность удалённых зависимостей
память
сеть
файловые дескрипторы

Наблюдаемость полезна, когда она ведёт от симптома запроса к дефицитному ресурсу.

Тонкая модульная инструментация Kora хорошо подходит для этого, потому что следует реальным границам технологий.

Производственные операции нуждаются в согласованных runbook'ах

Флотом легче управлять, когда расследование стандартизировано.

Например:

1. Проверьте дашборд RED.
2. Проверьте состояние раскатки/готовности.
3. Откройте репрезентативную медленную/ошибочную трассировку.
4. Определите доминирующий дочерний span.
5. Откройте дашборд зависимостей/ресурсов.
6. Запросите логи по traceId.
7. Проверьте метрики отказоустойчивости.

Этот рабочий процесс может применяться во многих сервисах, если конвенции телеметрии согласованы.

Это преимущество платформы, гораздо большее, чем удобство любого отдельного API.

Наблюдаемость улучшает введение в проект

Разработчик, присоединяющийся к сервису, может изучить архитектуру, глядя на репрезентативные трассировки.

Трассировка фактически документирует:

HTTP-эндпоинт
бизнес-операцию
зависимость базы данных
удалённые сервисы

Метрики раскрывают, какие пути высокообъёмные.

Логи раскрывают важные бизнес-решения.

Наблюдаемость становится картой архитектуры времени выполнения.

Это особенно полезно, когда статические диаграммы устаревают.

Нейро-агенты выигрывают по той же причине

нейро-агент, диагностирующий производственную проблему, выигрывает от структурированных свидетельств.

Вместо догадок по коду он может коррелировать задержку маршрута, иерархию трассировки, длительность span базы данных, статус клиента и структурированные логи ошибок.

Явная сгенерированная архитектура Kora плюс стандартизированная телеметрия — сильная комбинация: модель может изучать и статический сгенерированный исходник, и живые операционные сигналы.

Телеметрия должна быть заслуживающей доверия

Плохая наблюдаемость хуже её отсутствия, когда она создаёт ложную уверенность.

Примеры включают метрику, инкрементируемую до фиксации транзакции, готовность, сообщающую о здоровье, пока требуемый ресурс непригоден, спаны, завершающиеся до фактического окончания работы, или логи, теряющие сбои из-за неверной конфигурации уровней.

Интеграция фреймворка может решить корректность инфраструктуры.

Бизнес-телеметрия всё равно нуждается в тестах и ревью.

Наблюдаемость не должна становиться бизнес-зависимостью

Инструментация не должна менять бизнес-результаты.

Сбой логирования не должен ронять платёж.

Отключение экспортёра трассировки не должно бесконечно блокировать HTTP-трафик.

Сбой бэкенда метрик не должен останавливать обработку запросов.

Конвейерам телеметрии нужна изоляция сбоев.

Наблюдаемость должна наблюдать систему, а не становиться причиной её недоступности.

Бэкенды мониторинга тоже будут падать

Prometheus может быть недостижим.

OpenTelemetry collector может лежать.

Отправка логов может падать.

Сервис обычно должен продолжать работать.

Вот почему буферизованный/асинхронный экспорт и метрики на основе scrape — полезные паттерны.

Инфраструктуре наблюдаемости нужна собственная модель отказоустойчивости.

Противодавление применимо к конвейерам телеметрии

У экспортёра трассировки может быть ограниченная очередь.

Аппендер логирования может насыщаться.

Системе нужны ясные политики:

отбрасывать?
блокировать?
сэмплировать?

Для большинства телеметрии приложения блокировка пользовательского трафика из-за медленного бэкенда трассировки нежелательна.

Сам конвейер телеметрии — часть операционной архитектуры.

Конвейер наблюдаемости нуждается в наблюдаемости

Зрелая платформа мониторит конвейер телеметрии:

отброшенные спаны
очередь экспортёра
ошибки отправки логов
сбои scrape
здоровье collector'а

Иначе отсутствующая телеметрия может быть ошибочно прочитана как здоровый сервис.

Потеря телеметрии сама по себе сигнал.

Системному порту нужна сетевая политика

Отдельный операционный порт безопаснее публичного раскрытия метрик, но «приватный» всё равно требует принуждения.

Метрики могут раскрывать внутренние имена маршрутов, версии сервисов, имена операций базы данных и топологию.

NetworkPolicy, правила брандмауэра, политика сервисной сетки или ACL инфраструктуры должны ограничивать доступ соответствующим образом.

Операционные эндпоинты тоже заслуживают дизайна безопасности.

Не усложняйте локальные пробы аутентификацией

В то же время требование сложного удалённого потока аутентификации для локальных kubelet-проб может создавать проблемы надёжности.

Распространённый дизайн — ограничение на уровне сети плюс простые локальные системные эндпоинты.

Точная модель угроз зависит от среды.

Важно рассматривать системный порт как инфраструктурный трафик с намеренной политикой.

Один запрос через весь стек Kora

Теперь сожмём всю архитектуру в один путь.

Клиент вызывает:

GET /users/982374723/dashboard

Публичный HTTP-сервер сопоставляет его с маршрутом:

GET /users/{id}/dashboard

HTTP-телеметрия начинает или продолжает трассировку и фиксирует тайминг запроса.

Структурированное логирование получает активный контекст трассировки.

Контроллер вызывает сервис приложения.

Сервис опционально создаёт бизнес-span:

user.dashboard.build

Репозиторий исполняет:

UserRepository.findById

Телеметрия базы данных создаёт дочернее наблюдение и фиксирует длительность запроса.

HTTP-клиент вызывает:

RecommendationClient.getRecommendations

Телеметрия клиента создаёт дочерний span, распространяет контекст трассировки, фиксирует метрики задержки/статуса и логирует согласно политике.

Нижестоящий сервис продолжает трассировку.

Ответ возвращается.

HTTP-наблюдение фиксирует статус и длительность и закрывается.

Метрики уровня флота обновляются.

Трассировка завершена.

Если бизнес-код испустил важное событие, лог несёт тот же идентификатор трассировки.

Тем временем системный порт сообщает о живости, готовности и метриках.

Если процесс получает сигнал завершения, корректное завершение останавливает новую работу и позволяет текущей работе слиться в пределах настроенного бюджета.

Это не набор функций наблюдаемости.

Это операционно связный жизненный цикл запроса.

Три основных сигнала отвечают на три разных вопроса

Полезное резюме:

Метрики

Что происходит во многих операциях?

Примеры включают p95-задержку, частоту запросов, частоту ошибок, насыщение пула и частоту запасных вариантов.

Трассировка

Что произошло с этой конкретной операцией?

Примеры включают, какая зависимость доминировала в задержке, где возникла ошибка и какие операции выполнялись параллельно.

Логи

Что приложение решило или наблюдало?

Примеры включают выбор запасного варианта, отклонение бизнес-инварианта и неожиданное состояние.

Пробы отвечают на другой вопрос платформы:

Должен ли этот процесс получать трафик или быть перезапущен?

Каждый сигнал становится сильнее, когда его не заставляют делать работу другого сигнала.

Производственный сервис на Kora должен уметь отвечать на эти вопросы

Вижу ли я частоту HTTP, ошибки и задержку?
Вижу ли я задержку исходящих зависимостей?
Вижу ли я длительность запросов к базе данных?
Могу ли я коррелировать логи с трассировками?
Могу ли я изучить один запрос от начала до конца?
Ограничены ли метки метрик?
Маскируются ли секреты?
Вижу ли я активность повторов, запасных вариантов и breaker'ов?
Корректны ли семантически живость и готовность?
Приватны ли операционные эндпоинты?
Сливает ли корректное завершение запросы?
Могут ли экспортёры телеметрии падать без срыва трафика?
Проверяют ли тесты операционный контракт?

Если на несколько ответов «нет», добавление ещё одного дашборда, вероятно, не первый приоритет.

Сама архитектура наблюдаемости нуждается в работе.

Заключение

Наблюдаемость в Kora наиболее полезна, когда она понимается как часть производственной архитектуры фреймворка, а не как отдельная функция мониторинга.

Запрос входит через HTTP-сервер. Сервер устанавливает наблюдаемую границу. Код приложения выполняет бизнес-работу. Исходящие клиенты и репозитории создают собственные дочерние наблюдения. Контекст трассировки соединяет операции. Micrometer фиксирует агрегированное поведение. OpenTelemetry восстанавливает пути отдельных запросов. Структурированные логи добавляют семантические решения и несут корреляцию трассировки. Пробы раскрывают состояние процесса и допуска трафика на системном порту. Жизненный цикл и корректное завершение определяют, что происходит, когда экземпляр выводится из обслуживания.

Весь запрос можно представить так:

span HTTP-сервера
       ↓
span сервиса / бизнеса
       ↓
 ┌───────────────┐
 ↓               ↓
HTTP-клиент    репозиторий
span           span БД
 ↓               ↓
удалённый      база данных
сервис

в то время как те же операции питают метрики:

задержка HTTP
ошибки HTTP
задержка клиента
статус клиента
длительность базы данных
состояние пула соединений
бизнес-счётчики
метрики JVM / процесса

а логи остаются коррелированными через:

traceId
spanId
структурированные поля событий

Это основная идея наблюдаемости по проектированию.

Метрики должны обнаруживать.

Трассировки должны локализовать.

Логи должны объяснять.

Пробы должны управлять решениями о трафике и перезапуске.

Корректное завершение должно сохранять корректность, пока экземпляр покидает флот.

Фреймворк должен предоставлять инфраструктурную телеметрию там, где у него лучший контекст, в то время как код приложения добавляет бизнес-смысл, который фреймворк не может знать. Это разделение сохраняет наблюдаемость полезной, не заполняя бизнес-код телеметрической обвязкой.

Это также согласуется с более широкой архитектурой Kora: явные модули, тонкие абстракции, сгенерированная инфраструктура, одна связная модель через HTTP, клиенты, репозитории, обмен сообщениями и жизненный цикл.

Производственный сервис не должен ждать инцидента, чтобы стать наблюдаемым.

К моменту начала инцидента запрос уже произошёл.

Телеметрия должна быть там первой.