Алексей Меленчук
вся записка
дата
объём 5 мин чтения

Логи, метрики, трассировка: что из трёх вам действительно нужно

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

Коротко. Метрики отвечают «что и когда», трассировка — «где», логи — «почему». Порядок внедрения обратный порядку моды: сначала метрики, потом логи, трассировка последней.

«Три столпа наблюдаемости» — удачное название и вредная упаковка. Удачное, потому что запоминается. Вредное, потому что подразумевает комплект: возьмите все три и будет наблюдаемость.

Они отвечают на разные вопросы и стоят очень по-разному. Разберём, что каждый умеет, и в каком порядке их брать.

Три разных вопроса

Метрики отвечают «что происходит и когда началось». Числовые ряды: частота запросов, доля ошибок, задержка по перцентилям, глубина очереди. Дёшевы: агрегация происходит на месте, в хранилище уезжают числа. Хранятся годами, потому что занимают мало.

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

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

Незаменима, когда сервисов много. При одном сервисе даёт мало сверх того, что и так видно, — а стоит прилично: инструментирование, агент, хранилище, сэмплирование.

Логи отвечают «почему». Единственное место, где есть контекст: какие параметры пришли, какую ветку выбрали, что вернул внешний сервис. Самые дорогие из трёх — объём растёт линейно с трафиком, и хранение съедает бюджет быстрее всего.

Порядок, который я считаю правильным

Обратный тому, в котором это обычно берут.

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

Без метрик вы узнаёте об аварии от пользователя. Всё остальное вторично по сравнению с этим.

Логи вторыми, но структурированные. Не текстовая строка, а объект с полями. Разница проявляется в момент, когда нужно найти все запросы одного пользователя за час: по структурированному полю это запрос, по тексту — регулярка, которая ломается на следующей неделе.

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

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

Про уровни логирования

Отдельная тема, где принято делать неправильно.

Уровень должен означать «кто на это реагирует», а не «насколько мне кажется важным».

  • ERROR — сломалось, нужен человек. Каждая запись должна быть чем-то, на что кто-то посмотрит.
  • WARN — не сломалось, но ненормально. Сработал предохранитель, повтор помог, очередь растёт.
  • INFO — событие бизнес-уровня. Заказ создан, платёж проведён.
  • DEBUG — подробности, в проде выключены.

Симптом, что уровни поехали: в ERROR каждый день сотни записей, и никто их не читает. Тогда ERROR перестал что-либо значить, и в момент настоящей аварии он не отличается от фона.

Ошибка валидации пользовательского ввода — не ERROR. Система отработала правильно. Это INFO, максимум WARN, если такого стало подозрительно много.

Кардинальность — почему метрики внезапно дорожают

Одна вещь, на которой обжигаются все, кто берёт метрики впервые.

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

Метрика http_requests_total с метками «метод» и «код ответа» — это несколько десятков рядов. Добавили метку «путь» с нормализацией — несколько сотен, всё ещё нормально. Добавили идентификатор пользователя — столько рядов, сколько пользователей, и хранилище ложится.

Правило простое и жёсткое: в метках не бывает идентификаторов. Ни пользователя, ни заказа, ни запроса. Всё, что уникально для конкретного события, живёт в логах и трассировке, где хранение устроено под это.

Особенно коварен путь без нормализации: /orders/12345 создаёт ряд на каждый заказ. Нормализовать надо до шаблона /orders/{id}, и делать это на уровне сбора, а не надеяться на дисциплину.

Что съедает бюджет

Логи. Всегда логи.

Три вещи, которые сокращают объём на порядок и почти ничего не стоят:

  1. Не логировать успешный путь подробно. Одна запись на запрос с итогом, а не пятнадцать записей о каждом шаге. Подробности — на DEBUG, включается точечно.
  2. Сэмплировать повторяющееся. Тысяча одинаковых ошибок за минуту не информативнее десяти со счётчиком.
  3. Разный срок хранения. ERROR — долго, INFO — неделю, DEBUG — сутки. Единый срок для всего означает, что вы платите за отладочные записи как за аварийные.

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