# Стратегия логирования событий, которая сокращает расходы и ускоряет отладку

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

## Почему больше логов создаёт больше путаницы

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

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

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

Повторяющиеся логи делают больше, чем расходуют внимание. Они размывают причину и следствие. Если вызов базы данных упал один раз, а пять сервисов логируют ту же ошибку разными словами, у команды будет шесть подсказок и меньше ясности. Во время простоя этот шум легко может стоить 20–30 лишних минут.

Это ещё и дорого. Большие объёмы логов заполняют хранилище, увеличивают индексирование и замедляют поиск. Стартапы чувствуют это рано, потому что счёт за логи растёт тихо. Одна шумная фонова задача или болтливый API могут превратить небольшой ежемесячный платёж в неприятный сюрприз.

Малые команды ощущают это сильнее всего. Если три человека поддерживают продукт, у них нет времени читать тысячи строк, чтобы найти одно пропущенное поле, один некорректный деплой или один request ID, который объясняет проблему. Компактной продакшен-команде нужны меньше и яснее события.

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

## На что должно отвечать полезное событие

Полезное событие должно экономить время в момент сбоя. Если уставший инженер или основатель читает его во время инцидента, он должен понять, что произошло, не открывая пять дашбордов. Простая формулировка побеждает. "payment capture failed" говорит гораздо больше, чем "request error".

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

Начните с идентификации. Каждое событие должно называть объект: user ID, account ID, order ID, request ID или job ID. Не нужны все возможные поля. Нужно достаточно, чтобы проследить одно реальное действие от начала до конца.

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

Событие должно показывать результат. Минимум — успех или провал. Когда что-то падает, добавьте короткий код ошибки, который остаётся постоянным во времени. Читаемый текст полезен, но коды проще искать и группировать. `auth_token_expired` работать намного удобнее, чем "bad response from service".

Наконец, добавьте небольшое количество деталей, которые нужны человеку, чтобы решить, что делать дальше. Во многих случаях этого достаточно: `duration_ms`, `attempt`, `retryable` и одно бизнес-поле вроде `invoice_id` или `amount_cents`.

Возьмём ночную задачу выставления счетов. Одно событие может рассказать всю историю: `job_id`, `customer_id`, воркер, который её запустил, результат, код ошибки и информация о том, поможет ли повторная попытка. Этого достаточно для отладки инцидента. Это также спасает от хранения десяти туманных строк логов, которые почти ничего не говорят.

## Начинайте с вопросов инцидента, а не с объёма логов

Хорошее логирование начинается с тех же вопросов, которые люди задают, когда что-то ломается. Команды редко спрашивают: "Сколько строк мы сохранили?" Они спрашивают: "Что упало?" "Кто видел это?" "Когда это началось?" и "Сработал ли повтор?"

Запишите эти вопросы о простое сначала. Если строка лога не помогает ответить на один из них, ей, вероятно, не место.

### Превращайте вопросы в события

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

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

Этот подход также упрощает удаление слабых лог-строк. "entered function", "processing request" или случайный debug-текст может казаться безопасным, но они ничего не отвечают во время инцидента. Они стоят денег и замедляют поиск.

Сохраняйте имена событий стабильными между сервисами. Если один сервис пишет `payment_failed`, а другой — `payment.error` для того же события, команда тратит время на перевод имён вместо исправления проблемы. Выберите один стиль именования и придерживайтесь его.

Понятные имена вроде `checkout_started`, `payment_authorization_failed` и `retry_completed` легко просматривать, легко запрашивать и просто объяснить новым коллегам.

## Как проектировать события шаг за шагом

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

Затем пройдитесь по потоку и отметьте моменты, где система принимает решение. Пройдёт ли валидация email? Существовал ли уже аккаунт? Сохранилась ли запись? Инциденты часто прячутся в этих точках решений.

Простой порядок разработки работает хорошо. Сначала залогируйте одно событие при успехе шага. Затем — одно событие при неудаче того же шага. Назовите событие по решению, а не по экрану. Прикрепляйте одинаковый контекст каждый раз: request ID, user ID (если есть) и имя сервиса. После этого прогоните поток в staging и проследите один успешный и один неуспешный кейс.

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

Общие поля делают основную работу. request ID позволяет следить за действием через несколько сервисов. user ID помогает поддержке или инженерам проверить реальный случай без догадок. Имя сервиса показывает, откуда пришло событие, когда одно действие затрагивает фронтенд, auth-сервис и фонового воркера.

Держите тело события маленьким и читаемым. Обычно не нужны все переменные, payload или повторные попытки. Четыре ясных события вокруг реальных решений бьют двадцать шумных, которые мало о чём говорят.

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