Логи, метрики, трассировка: что из трёх вам действительно нужно
Три столпа продают набором. Они отвечают на разные вопросы, стоят по-разному, и начинать надо не с того, что модно.
Коротко. Метрики отвечают «что и когда», трассировка — «где», логи — «почему». Порядок внедрения обратный порядку моды: сначала метрики, потом логи, трассировка последней.
«Три столпа наблюдаемости» — удачное название и вредная упаковка. Удачное, потому что запоминается. Вредное, потому что подразумевает комплект: возьмите все три и будет наблюдаемость.
Они отвечают на разные вопросы и стоят очень по-разному. Разберём, что каждый умеет, и в каком порядке их брать.
Три разных вопроса
Метрики отвечают «что происходит и когда началось». Числовые ряды: частота запросов, доля ошибок, задержка по перцентилям, глубина очереди. Дёшевы: агрегация происходит на месте, в хранилище уезжают числа. Хранятся годами, потому что занимают мало.
Чего не умеют: сказать, что случилось с конкретным запросом. Метрика показывает, что девяносто девятый перцентиль вырос до двух секунд. Какой именно запрос тормозил — она не знает.
Трассировка отвечает «где именно время». Один запрос, разложенный по участкам с длительностями: сколько в вашем коде, сколько в базе, сколько в чужом сервисе.
Незаменима, когда сервисов много. При одном сервисе даёт мало сверх того, что и так видно, — а стоит прилично: инструментирование, агент, хранилище, сэмплирование.
Логи отвечают «почему». Единственное место, где есть контекст: какие параметры пришли, какую ветку выбрали, что вернул внешний сервис. Самые дорогие из трёх — объём растёт линейно с трафиком, и хранение съедает бюджет быстрее всего.
Порядок, который я считаю правильным
Обратный тому, в котором это обычно берут.
Метрики первыми. Четыре золотых сигнала — задержка, трафик, ошибки, насыщенность — плюс глубина очередей и состояние пулов. Это меньше дня работы с готовыми библиотеками и закрывает вопрос «работает ли вообще и когда сломалось».
Без метрик вы узнаёте об аварии от пользователя. Всё остальное вторично по сравнению с этим.
Логи вторыми, но структурированные. Не текстовая строка, а объект с полями. Разница проявляется в момент, когда нужно найти все запросы одного пользователя за час: по структурированному полю это запрос, по тексту — регулярка, которая ломается на следующей неделе.
Обязательное поле — идентификатор запроса, сквозной через все сервисы. Он стоит почти ничего и даёт восемьдесят процентов пользы трассировки: собрать все записи одного запроса можно и без трассировочной системы.
Трассировка третьей, когда сервисов стало больше трёх. До этого момента вопрос «где время» решается логами с длительностями и метрикой по внешним вызовам.
Про уровни логирования
Отдельная тема, где принято делать неправильно.
Уровень должен означать «кто на это реагирует», а не «насколько мне кажется важным».
ERROR— сломалось, нужен человек. Каждая запись должна быть чем-то, на что кто-то посмотрит.WARN— не сломалось, но ненормально. Сработал предохранитель, повтор помог, очередь растёт.INFO— событие бизнес-уровня. Заказ создан, платёж проведён.DEBUG— подробности, в проде выключены.
Симптом, что уровни поехали: в ERROR каждый день сотни записей,
и никто их не читает. Тогда ERROR перестал что-либо значить,
и в момент настоящей аварии он не отличается от фона.
Ошибка валидации пользовательского ввода — не ERROR. Система отработала
правильно. Это INFO, максимум WARN, если такого стало подозрительно
много.
Кардинальность — почему метрики внезапно дорожают
Одна вещь, на которой обжигаются все, кто берёт метрики впервые.
Стоимость системы метрик определяется не числом записей, а числом временных рядов. Ряд — это уникальная комбинация имени метрики и значений всех её меток.
Метрика http_requests_total с метками «метод» и «код ответа» —
это несколько десятков рядов. Добавили метку «путь» с нормализацией —
несколько сотен, всё ещё нормально. Добавили идентификатор
пользователя — столько рядов, сколько пользователей, и хранилище
ложится.
Правило простое и жёсткое: в метках не бывает идентификаторов. Ни пользователя, ни заказа, ни запроса. Всё, что уникально для конкретного события, живёт в логах и трассировке, где хранение устроено под это.
Особенно коварен путь без нормализации: /orders/12345 создаёт ряд
на каждый заказ. Нормализовать надо до шаблона /orders/{id},
и делать это на уровне сбора, а не надеяться на дисциплину.
Что съедает бюджет
Логи. Всегда логи.
Три вещи, которые сокращают объём на порядок и почти ничего не стоят:
- Не логировать успешный путь подробно. Одна запись на запрос
с итогом, а не пятнадцать записей о каждом шаге. Подробности —
на
DEBUG, включается точечно. - Сэмплировать повторяющееся. Тысяча одинаковых ошибок за минуту не информативнее десяти со счётчиком.
- Разный срок хранения.
ERROR— долго,INFO— неделю,DEBUG— сутки. Единый срок для всего означает, что вы платите за отладочные записи как за аварийные.
Где я могу быть неправ. Если у вас регуляторное требование хранить всё — весь разговор про объём отменяется, и остаётся только вопрос бюджета. И если сервис один, а команда — два человека, три столпа могут оказаться избыточными все три: метрики плюс внятные логи закроют почти всё, а трассировку стоит отложить до момента, когда станет непонятно, где время.