$ sudo teach|
    $ sudo teach|
    IT school
  • Telegram
  • Партнёрам
  • Все курсы
$ sudo teach IT
OOO "SALEPROFIT"Контакты и реквизитыIT-Park Logo

Школа

  • Блог
  • Проверить сертификат

Сотрудничество

  • Стать учителем
  • Партнёрская программа
  • О проекте

Право

  • Оферта
  • Политика конфиденциальности

© 2023–2026 $ sudo teach IT™. All Rights Reserved. Public user contributions licensed under CC BY-SA 4.0 license with attribution required
TelegramGitHubYouTube
ГлавнаяБлогНаблюдаемость для API: трейсинг, корреляция запросов и измеримые SLA

Наблюдаемость для API: трейсинг, корреляция запросов и измеримые SLA

$ sudo teach IT
·22 июля 2026 г.·10 мин·50
Наблюдаемость для API: трейсинг, корреляция запросов и измеримые SLA

Настроим подход к логам/метрикам/трейсам так, чтобы инциденты находились за минуты, а не “по ощущениям”. Рассмотрим корреляцию по request-id и практики формирования событий.

Содержание
Из чего состоит наблюдаемость APIПочему инциденты «по ощущениям» не масштабируютсяКорреляция запросов: request-id как базовый каркасrequest-id vs trace-idГде генерировать request-idПример: middleware для request-id и логовПередача request-id в межсервисных вызовахТрейсинг: как сделать видимым путь запросаЧто считать «корректным» трейсингомOpenTelemetry как практичный стандартПример: instrument стек OpenTelemetry (концептуально)Как увязать request-id и trace-idИзмеримые SLA/SLO: от сигналов к обязательствамПочему «SLA» без измерения — это юридический документ без пользыПримеры SLO для APIОткуда берутся метрики: согласование с трейсингомФормирование событий: логика «инцидент — не шум»Почему одних алертов малоПрактика: классификация ошибок и атрибуты событийТипичные ошибки при формировании событийПрактический сценарий: расследование за минутыШаг 1. Метрика и алертШаг 2. Корреляция по trace/requestШаг 3. Локализация первопричиныШаг 4. Подтверждение влияния на SLAКак настроить логирование, чтобы оно действительно помогалоЛоги должны быть структурнымиНе логируйте всё подрядПравило: ошибки — всегда с классификациейУправление «инцидентностью»: ретраи, дедлайны и корректная картинаРетраи меняют семантику “одного запроса”Дедлайны (timeouts) должны

API — это не просто набор эндпоинтов. Это распределённая система с сетью, очередями, балансировщиками, кешами, базами данных, внешними сервисами и множеством отказов на пути от запроса до ответа. В таких условиях «посмотреть логи» редко работает как стратегия. Нужна наблюдаемость (observability): возможность быстро понять что случилось, где произошло, почему, и насколько это влияет на SLA.

В этой статье разберём практический подход к наблюдаемости для API, который помогает находить инциденты не «по ощущениям», а за минуты: трейсинг запросов, корреляция по request-id, формирование событий и метрик, а также связка с измеримыми SLA/SLO. Будем говорить не абстрактно, а через конкретные практики и примеры конфигурации.


Из чего состоит наблюдаемость API

Наблюдаемость обычно описывают тремя «китами»:

  • Логи — текстовые события, удобные для диагностики, но сложно коррелировать и искать причинно-следственные связи в распределённой системе.
  • Метрики — агрегированные численные показатели (latency, error rate, throughput), полезны для контроля и оповещений, но без контекста могут быть «серой коробкой».
  • Трейсы (трейсинг) — распределённые трассы, которые описывают путь запроса через сервисы и позволяют видеть причинные цепочки.

Для API важна не просто настройка трёх типов данных, а их согласованность: общий идентификатор запроса, единая семантика ошибок, одинаковые правила атрибуции (какой сервис что измеряет), и понятные связки «тревога → трасса → причина → влияние на пользователей».

Почему инциденты «по ощущениям» не масштабируются

Когда в проде что-то сломалось, обычно происходит следующее:

  1. Приходит алерт по метрике (например, вырос 5xx).
  2. Команда ищет логи по времени и IP.
  3. В попытках угадать причину просматривает много «несвязанных» событий.
  4. Если несколько сервисов участвуют в запросе, то поиск начинает превращаться в ручной комбинаторный ад.

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


Корреляция запросов: request-id как базовый каркас

request-id vs trace-id

Есть два похожих, но разных концепта:

  • request-id — идентификатор на уровне «внешнего запроса» (обычно на входе в API). Он помогает связать действия в пределах одного логического запроса.
  • trace-id — идентификатор трассы (в терминах OpenTelemetry/Distributed Tracing). Он объединяет span-ы (отрезки выполнения) в дерево/граф.

На практике их можно связать:

  • request-id используется как «человеческий» и удобный для логов корреляционный ключ.
  • trace-id — как ключ трассировки для инструментов трейсинга.

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

Где генерировать request-id

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

  • Если у вас есть API Gateway / Ingress — обычно request-id генерируется там или прокидывается от клиента.
  • Если у вас напрямую экспонирован сервис — генерируйте request-id в middleware на входе.

Дополнительно важно: request-id должен быть устойчивым до конца жизни запроса, включая ретраи и асинхронные ветки (там — по отдельным правилам).

Пример: middleware для request-id и логов

Ниже пример на Node.js/Express, демонстрирующий идею: извлекаем X-Request-Id (или генерируем), кладём в контекст и используем в логах.

code
import express from "express";
import { randomUUID } from "crypto";

const app = express();

function requestIdMiddleware(req, res, next) {
  const incoming = req.headers["x-request-id"];
  const requestId = typeof incoming === "string" && incoming.length > 0
    ? incoming
    : randomUUID();

  req.requestId = requestId;
  res.setHeader("X-Request-Id", requestId);

  // Если используете pino/http-log-context или async hooks — лучше кладите в контекст.
  // Здесь для простоты — в объект запроса.
  next();
}

app.use(requestIdMiddleware);

app.get("/v1/users/:id", (req, res) => {
  const requestId = req.requestId;

  try {
    // ... бизнес-логика
    res.json({ ok: true, requestId });
  } catch (e) {
    console.error("error", { requestId, err: e });
    res.status(500).json({ error: "internal", requestId });
  }
});

app.listen(3000);

Ключевые моменты:

  • request-id возвращаем клиенту (удобно для поддержки и корреляции).
  • используем request-id в логах и ответах.
  • дальше — передаём заголовок во все исходящие запросы к другим сервисам.

Передача request-id в межсервисных вызовах

В микросервисной среде запросы часто идут через HTTP/gRPC. В любом клиенте (fetch/axios/http client) добавляйте заголовок.

Пример на fetch:

code
async function callUserService(requestId, userId) {
  const resp = await fetch(`http://users/v1/users/${userId}`, {
    headers: {
      "X-Request-Id": requestId
    }
  });

  if (!resp.ok) {
    const text = await resp.text().catch(() => "");
    throw new Error(`users service failed: ${resp.status} ${text}`);
  }
  return resp.json();
}

На стороне получателя request-id должен попасть в его контекст и логирование.


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

Что считать «корректным» трейсингом

Хорошая трасса для API должна содержать:

  1. Один корневой span на входящий запрос (server span).
  2. span-ы на ключевые операции: вызовы БД, кеша, внешних сервисов, вычисления (если они длительные), публикация событий.
  3. Семантические атрибуты: метод, путь, статус, downstream service, тип ошибки, размер ответа (если нужно), tenant/user (с оглядкой на приватность).
  4. Ошибки как часть модели: исключения и ошибки уровня span должны быть промаркированы, чтобы они отображались в UI и учитывались при построении метрик.

Плохая трасса — это когда span-ы не совпадают с реальными операциями или отсутствует связность.

OpenTelemetry как практичный стандарт

OpenTelemetry стал де-факто основой трейсинга в облачных средах: он стандартизирует форматы данных, что снижает «зоопарк интеграций». Критично лишь внедрить его правильно: не только включить SDK, но и добавить согласованные атрибуты и корректную передачу контекста.

Пример: instrument стек OpenTelemetry (концептуально)

Ниже — псевдореалистичный фрагмент на Python с opentelemetry-sdk и концепцией автодокументирования серверных spans. Конкретные версии пакетов могут отличаться, но логика одинакова.

code
from opentelemetry import trace
from opentelemetry.trace import Status, StatusCode

tracer = trace.get_tracer(__name__)

def handle_request(request):
    with tracer.start_as_current_span("http.server", kind=trace.SpanKind.SERVER) as span:
        span.set_attribute("http.method", request.method)
        span.set_attribute("http.route", request.path)

        try:
            # downstream call
            with tracer.start_as_current_span("db.query", kind=trace.SpanKind.CLIENT) as db_span:
                # db query...
                pass

            span.set_attribute("http.status_code", 200)
            return {"status": 200}

        except Exception as e:
            span.record_exception(e)
            span.set_status(Status(StatusCode.ERROR, str(e)))
            span.set_attribute("http.status_code", 500)
            return {"status": 500}

В реальном проекте вы обычно используете авто-инструментацию HTTP/DB клиентов, но принцип тот же: span должен отражать операцию и быть корректно помечен при ошибке.

Как увязать request-id и trace-id

В идеале:

  • в логах: request-id
  • в трассах: trace-id
  • в UI/аналитике: запрос можно найти по request-id, а затем открыть trace по trace-id (или наоборот).

На практике часто удобно дублировать request-id в атрибут трассы (например, request.id = ...), и тогда вы можете искать трассы по request-id и не зависеть от того, чем пользователь «подсветил» контекст.


Измеримые SLA/SLO: от сигналов к обязательствам

Почему «SLA» без измерения — это юридический документ без пользы

SLA — это обещание доступности/скорости. Но без SLO (Service Level Objectives) и без измерения конкретных пользовательских сценариев SLA превращается в декларацию.

Чтобы SLA стали рабочими, вам нужен минимум:

  • единая метрика качества (например, success rate и latency на конкретном маршруте);
  • окно измерения (например, последние 5 минут или 30 дней);
  • чёткая формула (что считается успешным, какие коды исключаются, как трактовать таймауты).

Для API типичная модель: SLO по проценту успешных ответов и/или latency p95/p99.

Примеры SLO для API

  1. Availability / Success rate:
  • SLO: ≥ 99.9% запросов к /v1/* успешно (2xx–3xx) в интервале 30 дней.
  • Обязательно определить, что делать с 4xx/5xx: например, 4xx считаем клиентскими ошибками, исключаем из «инцидентности», 5xx и таймауты — считаем отказами.
  1. Latency:
  • SLO: p95 latency ≤ 200 ms для эндпоинта /v1/users/:id за 5-минутное окно.
  • Важно: latency измеряйте как «end-to-end» от принятия запроса до отправки ответа, а не только время DB.
  1. Error budget:
  • Отдельно считать budget и тратить его на подтверждённые инциденты, а не на любые шумные колебания.

Откуда берутся метрики: согласование с трейсингом

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

  • Метрики считаются из той же точки времени, что и корневой span.
  • Теги/лейблы метрик включают route, method, статус-код и критические измерения (tenant — если оправдано).
  • В событиях/трейсах есть одинаковая классификация ошибок.

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

  1. Метрика упала: например, p95 вырос.
  2. Смотрите распределение ошибок по классификации.
  3. Открываете top traces за период, выбираете лидирующий downstream span.
  4. Коррелируете запросы по request-id и подтверждаете влияние.

Формирование событий: логика «инцидент — не шум»

Почему одних алертов мало

Метрики будут всегда «шуметь». Вопрос — как превращать это в управляемые расследования.

Событийная модель (event semantics) нужна для:

  • группировки инцидентов (один корень — много симптомов);
  • автоматического обогащения контекста;
  • сокращения времени от «алерт пришёл» до «мы знаем первопричину».

Практика: классификация ошибок и атрибуты событий

Определите минимальный набор полей для событий:

  • service.name
  • endpoint / route
  • http.method
  • http.status_code
  • error.type (например: timeout, db_error, upstream_5xx, validation_error)
  • upstream.service (если ошибка вниз по цепочке)
  • request.id
  • trace.id (если есть)
  • duration_ms
  • user_context (по возможности, с учётом приватности)

Тогда инциденты можно агрегировать: например, «таймауты к upstream X» или «рост db deadlocks».

Типичные ошибки при формировании событий

  • Смешивание транспортных и бизнес-ошибок. Таймаут сети — это одно, валидация входа — другое.
  • Неполные лейблы. Если вы не тэгируете route и upstream, расследование будет ручным.
  • Отсутствие единого словаря error.type. «Timeout», «timed out», «ETIMEDOUT» превращаются в десятки категорий.
  • События без request-id / trace-id. Тогда корреляция невозможна.

Практический сценарий: расследование за минуты

Рассмотрим типичный кейс: растут 5xx на публичном API.

Шаг 1. Метрика и алерт

Вы видите, что 5xx_rate по route /v1/orders вырос с 0.2% до 2.5% за 7 минут. Дополнительно алерт по p95 latency с 180 ms до 900 ms.

Сразу проверяете, не является ли это комбинацией нескольких причин. Например, распределите по error.type и upstream.service.

Шаг 2. Корреляция по trace/request

Открываете выборку трасс по интервалу инцидента. Фильтруете по:

  • http.route = /v1/orders
  • http.status_code = 500 (или ошибка сервера)
  • сортируете по длительности root span
  • смотрите доминирующий downstream span

Часто вы увидите, что корневой span «ждёт» upstream.payment.charge или «залипает» в db.query.

Если вы также используете request-id, можно взять один из наиболее характерных запросов и посмотреть связанные логи в нескольких сервисах по одному X-Request-Id.

Шаг 3. Локализация первопричины

В корректной системе расследование сводится к одному из паттернов:

  • upstream отдаёт 5xx → классифицируем как upstream_5xx;
  • upstream таймауты → timeout;
  • рост latency из-за lock contention → db_contention;
  • неверные входные данные, приводящие к ошибкам в downstream → validation_error (в зависимости от модели).

В трассе это видно как сегменты с ошибкой/статусом на конкретном span. В логах — как набор событий с одним request-id.

Шаг 4. Подтверждение влияния на SLA

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

  • сколько запросов попали в период инцидента;
  • сколько из них были успешны;
  • какие эндпоинты пострадали;
  • какие SLO нарушены и на сколько.

Именно здесь трейсинг и логирование становятся «инженерными», а не «театром лога»: вы видите не просто, что «что-то тормозит», а как это отражается на формализованных SLA/SLO.


Как настроить логирование, чтобы оно действительно помогало

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

Текстовые логи удобны для чтения глазами, но корреляция и аналитика требуют структуры. Практический стандарт:

  • JSON-формат логов
  • поля: timestamp, level, service, request.id, trace.id, message, error, duration_ms (где уместно)

Если вы внедряете структурные логи, то фильтрация по request-id становится мгновенной, а не «grep-ом по файлам».

Не логируйте всё подряд

Слишком много логов:

  • создаёт шум и повышает стоимость хранения/индексации;
  • ухудшает сигнал/шум;
  • может утекать PII.

Подход: логируйте события, которые участвуют в расследовании: ошибки, предупреждения о деградации, start/stop ключевых операций (с duration), и контекст.

Правило: ошибки — всегда с классификацией

Вместо «Something went wrong» используйте:

  • error.type
  • error.code (если есть)
  • exception.class (техническое)
  • downstream детали (что именно не удалось)

Управление «инцидентностью»: ретраи, дедлайны и корректная картина

Отдельная зона риска для API — ретраи и таймауты. Без них метрики могут казаться странными, а трассы — запутанными.

Ретраи меняют семантику “одного запроса”

Если клиент ретраит запрос, вы получите несколько корневых трасс. Корреляция только по request-id не всегда поможет, если request-id генерируется заново на каждом ретрае.

Что делать:

  • Если клиент умеет — пусть передаёт один request-id на весь процесс.
  • Если ретраите вы сами (между сервисами) — нужно явно помечать попытку (attempt=1..N) и сохранять общий корреляционный идентификатор.

В трассе это отражается span-ами попыток, а в событиях — классификацией timeout и количеством попыток.

Дедлайны (timeouts) должны

Войдите, чтобы поставить лайк и оставить комментарий.

Автор

$ sudo teach IT

Продолжите обучение

Все курсы
Python – для начинающих!

Python – для начинающих!

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

Перейти к курсу
Простыми словами о защищенном соединении

Простыми словами о защищенном соединении

Базовый курс для обычных людей: что такое защищённое соединение, как оно устроено, что оно умеет и чего не умеет, как выбрать сервис и настроить его на своих устройствах.

Перейти к курсу
Ботостроение Telegram

Ботостроение Telegram

Лёгкий, быстрый и доступный способ познакомиться с миром ботостроения в Telegram. Видео, конспекты, практика и помощь – всё у нас на курсе.

Перейти к курсу
Сервис, который не падает - как построить безопасную зашифрованную сеть

Сервис, который не падает - как построить безопасную зашифрованную сеть

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

Перейти к курсу

Другие статьи

Наблюдаемость с нуля: correlation-id, структурные логи и единый формат событий
наблюдаемость

Наблюдаемость с нуля: correlation-id, структурные логи и единый формат событий

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

25 июля 2026 г.
600
Индексы и планы запросов: ускоряем SQL, а не «угадываем»
индексы

Индексы и планы запросов: ускоряем SQL, а не «угадываем»

Разберём, как читать EXPLAIN/EXPLAIN ANALYZE и как корректно выбирать индексы. Научимся находить бутылочные горлышки в запросах и убирать лишние сортировки.

19 июля 2026 г.
590
Создание Telegram бота в 2026 легко и просто! Полный курсы!
создание

Создание Telegram бота в 2026 легко и просто! Полный курсы!

19 июня 2026 г.
1431
Как выбрать, куда идти в IT: веб, боты, данные — без хаоса
выбрать

Как выбрать, куда идти в IT: веб, боты, данные — без хаоса

Разберём самые понятные направления для новичка (веб, боты, аналитика/данные) и чем они отличаются по шагам обучения. В конце вы получите простой план, как проверить, что вам подходит, и какой стартовый путь выбрать.

5 октября 2026 г.
580
Путь от README до демо: как собрать портфолио разработчика, чтобы его хотели смотреть
путь

Путь от README до демо: как собрать портфолио разработчика, чтобы его хотели смотреть

Разберём структуру репозитория, как упаковать проект в понятную историю (проблема → решение → запуск → метрики качества) и какие артефакты повышают доверие: скринкасты, архитектурная схема, чек-лист продакшн-готовности.

17 сентября 2026 г.
1090
Как понять, какой язык программирования учить первым: простой тест и выбор
понять

Как понять, какой язык программирования учить первым: простой тест и выбор

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

2 октября 2026 г.
630

Комментарии

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

Поделитесь мнением о статье или задайте вопрос автору

Содержание

Из чего состоит наблюдаемость APIПочему инциденты «по ощущениям» не масштабируютсяКорреляция запросов: request-id как базовый каркасrequest-id vs trace-idГде генерировать request-idПример: middleware для request-id и логовПередача request-id в межсервисных вызовахТрейсинг: как сделать видимым путь запросаЧто считать «корректным» трейсингомOpenTelemetry как практичный стандартПример: instrument стек OpenTelemetry (концептуально)Как увязать request-id и trace-idИзмеримые SLA/SLO: от сигналов к обязательствамПочему «SLA» без измерения — это юридический документ без пользыПримеры SLO для APIОткуда берутся метрики: согласование с трейсингомФормирование событий: логика «инцидент — не шум»Почему одних алертов малоПрактика: классификация ошибок и атрибуты событийТипичные ошибки при формировании событийПрактический сценарий: расследование за минутыШаг 1. Метрика и алертШаг 2. Корреляция по trace/requestШаг 3. Локализация первопричиныШаг 4. Подтверждение влияния на SLAКак настроить логирование, чтобы оно действительно помогалоЛоги должны быть структурнымиНе логируйте всё подрядПравило: ошибки — всегда с классификациейУправление «инцидентностью»: ретраи, дедлайны и корректная картинаРетраи меняют семантику “одного запроса”Дедлайны (timeouts) должны