← назад к разделу

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

Причина не в том, что инструменты плохие. Каждый из трёх сигналов настроен правильно и отвечает на свой вопрос: метрика — «сколько и как быстро», трассировка — «где именно», логи — «что произошло». Проблема в том, что между ними нет мостов, и дежурный работает этим мостом сам.

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

Общий идентификатор: то, без чего ничего не связывается

Всё держится на одной вещи — сквозном идентификаторе запроса. Он рождается на входе в систему, едет через все сервисы и попадает в каждый сигнал: в трассировку как идентификатор трейса, в каждую строку лога как поле, а при желании и в метрику.

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

Проверить, что мост построен, можно за минуту. Возьмите любую строку лога боевого сервиса и попробуйте по её идентификатору найти трейс. Не нашли — дальше можно не читать, сначала надо починить это.

Две ошибки встречаются чаще всего. Первая — идентификатор есть не во всех сервисах: цепочка обрывается там, где кто-то не пробросил заголовок, и половина пути невидима. Вторая — поле называется по-разному: trace_id в одном сервисе, traceId в другом, X-Request-Id в третьем. Поиск по логам после этого превращается в угадывание, поэтому имя поля стоит зафиксировать в общем стандарте и проверять при ревью.

Exemplars: клик из графика прямо в трейс

Общий идентификатор связывает трассировку и логи. Метрика остаётся в стороне — в ней по определению нет отдельных запросов, только агрегаты.

Мостик между графиком и трассировкой называется exemplar. Идея простая: рядом со значением метрики можно приложить ссылку на один конкретный запрос, который в это значение попал. Смотрите на график задержки, видите всплеск — и проваливаетесь из точки на графике прямо в трейс того самого медленного запроса.

Выглядит это в выдаче метрик так:

http_server_requests_seconds_count 1.0 # {trace_id="4bf92f", span_id="00f067"} 1.0 1713310767.908

Всё, что после #, и есть exemplar: идентификаторы трейса и спана плюс отметка времени.

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

Вторая практическая: чтобы exemplars доехали, метрики нужно отдавать в формате OpenMetrics — в обычном текстовом формате Prometheus их просто нет. Проверяется одним запросом:

curl -H 'Accept: application/openmetrics-text; version=1.0.0' localhost:8081/actuator/prometheus

Если в ответе после значений появились комментарии с trace_id — мост работает.

Путь дежурного: четыре шага

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

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

Шаг второй — какой запрос. Из точки на графике проваливаемся в exemplar и получаем конкретный трейс. Если exemplars не настроены, тот же результат достигается вручную: берём отрезок времени и ищем в трассировке самые медленные или упавшие запросы этого эндпоинта.

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

Шаг четвёртый — что произошло. По идентификатору трейса достаём все строки логов этого запроса со всех сервисов. Это тот момент, ради которого всё строилось: вместо «поищем ошибки за последние десять минут» — точный список того, что случилось именно в этом запросе, по порядку.

Дальше расследование уходит вглубь: в план запроса, если виновата база; в снимок памяти, если сервис деградирует со временем; в разбор кода, если логика неверная.

Что положить в сам алерт

Половина потерянного времени тратится ещё до первого графика — пока дежурный вспоминает, что это за сервис и куда смотреть. Лечится тем, что кладётся в текст алерта.

Полезный алерт называет симптом со стороны клиента, а не техническую причину: «4% ошибок на оформлении заказа» вместо «error rate > 0.04». Дальше — ссылка на нужный дашборд с уже выставленным временем, чтобы не искать его руками. И ссылка на инструкцию: что это за срабатывание, какие бывают причины, что проверить в первую очередь.

Инструкция здесь не бюрократия. Алерт без неё будит человека, который не знает, что делать, — про это же говорит статья про SLO и алерты: если реакция на срабатывание всегда «посмотрел и ничего не сделал», такой алерт лучше удалить, он приучает не реагировать.

Три ловушки

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

Логи и трассировка живут разное время. Трейсы хранятся неделю, логи — три дня. Через пять дней после аварии половина расследования уже недоступна. Сроки хранения стоит согласовать между собой, а для аварий — сохранять выборку отдельно, до разбора.

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

Коротко

  • Три сигнала связываются одним сквозным идентификатором запроса: он должен быть в трассировке, в каждой строке лога и называться везде одинаково.
  • Проверка моста занимает минуту: взять строку лога и найти по ней трейс.
  • Exemplar — ссылка на конкретный запрос рядом со значением метрики; он не увеличивает мощность и требует формата OpenMetrics.
  • Путь дежурного: масштаб по графику → конкретный запрос через exemplar или поиск → место в трассировке → строки логов по идентификатору.
  • В алерте должны быть симптом со стороны клиента, ссылка на дашборд с нужным временем и инструкция.
  • Ловушки: выборка не сохранила нужный трейс, разные сроки хранения логов и трейсов, разъехавшиеся часы.

Что почитать дальше

  • Проброс контекста — как идентификатор переезжает между сервисами через HTTP и брокеры.
  • SLO и алерты — на что вообще стоит будить дежурного.
  • Профилирование и утечки памяти — куда идти, когда трейс показал деградацию во времени.
  • Метрики — почему идентификаторы нельзя класть в метки, а в exemplars можно.