28 мар. 2026 г.·7 мин чтения

Снижайте затраты на логирование, сохраняя ошибки, а не каждый успех

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

Снижайте затраты на логирование, сохраняя ошибки, а не каждый успех

Почему успешные логи быстро разрастаются

Большинство команд начинают с благих намерений. Они пишут в лог каждый 200 OK, каждую завершенную фоновую задачу и каждое сообщение «готово», потому что так кажется безопаснее. Сначала это почти не заметно. Но по мере роста трафика та же привычка превращается в тихий счет, который все время растет.

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

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

Во время инцидента люди открывают логи, чтобы ответить на простой вопрос: что сломалось, где и у кого? Если результаты поиска забиты бесконечными строками об успехе, полезные данные теряются среди них. Таймаут, плохой payload или шквал повторных попыток могут оказаться между тысячами строк, которые все говорят, что запрос завершился нормально.

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

Простой пример делает это очевидным. Представьте поток регистрации, который пишет «пользователь успешно создан» для каждого обычного аккаунта. В загруженный день эта строка почти ничего не сообщает, но может появляться сотни тысяч раз. Если шаг с email ломается у 0,5% пользователей, эти немногие записи об ошибках важнее, чем гора здоровых подтверждений вокруг них.

Если хотите сократить затраты на логирование, начинайте с объема, а не с редких крайних случаев. В большинстве систем самый большой поток создают именно здоровые запросы. Обычно это логи успешных запросов, завершенные cron-задачи, worker-процессы очередей, которые сообщают о каждом обычном завершении, и health checks, которые весь день пишут одно и то же.

Что заслуживает полной детализации

Если хотите сократить затраты на логирование, оставляйте подробности там, где они действительно окупаются: при ошибках. Когда запрос ломается, короткая строка с «500» мало помогает. Нужны данные, которые потом позволят восстановить полную картину, даже если никто не видел инцидент вживую.

Начните с базового набора, который нужен каждой ошибке. Сохраняйте точный timestamp, request ID или trace ID, имя сервиса, маршрут или имя задачи, версию сборки и stack trace. Добавьте финальное сообщение об ошибке и статус-код, который получил пользователь или вызывающая сторона. Это даст команде быстрый путь от алерта к первопричине.

Некоторым ошибкам нужен дополнительный контекст, потому что первое сообщение часто слишком размытое. Записывайте такие детали, как:

  • количество повторных попыток и состояние backoff
  • длина таймаута и какой именно зависимый сервис не ответил
  • ошибки валидации с именем поля и правилом, которое не прошло
  • upstream status codes из баз данных, очередей, auth-сервисов или платежных провайдеров
  • безопасный бизнес-контекст, например tenant ID, регион или состояние feature flag

Последний пункт особенно важен. Лог должен объяснять, почему произошел сбой, а не просто подтверждать, что он произошел. Если worker упал из-за того, что Redis не ответил после трех повторов, напишите это прямо. Если API-запрос не прошел, потому что в payload не хватало обязательного поля, запишите имя поля и правило, а не весь сырой payload.

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

Правила приватности действуют даже тогда, когда приложение в огне. Не храните пароли, API keys, session tokens, сырые персональные данные или полные платежные данные. Маскируйте, хэшируйте или удаляйте их до того, как лог покинет приложение. Если поддержке позже нужно сопоставить запись пользователя, обычно достаточно частичного идентификатора.

Хорошие логи ошибок — точные, компактные и безопасные. Когда что-то ломается в 10:42, команда должна видеть, что именно сломалось, где сломалось и что попробовать дальше, без стены шума.

Как обращаться со здоровым трафиком

Большинству здоровых запросов не нужна полноценная запись в лог. Если 50 000 запросов завершаются именно так, как ожидалось, хранение 50 000 почти одинаковых строк об успехе добавляет расходы, а не понимание. Команды, которые хотят сократить затраты на логирование, обычно быстрее всего выигрывают именно здесь.

Для обычного трафика храните итоги и время в метриках, а не в сырых логах. Считайте успешные запросы по маршруту, статус-коду, региону и версии релиза. Отслеживайте еще и перцентили задержки и частоту повторов. Эти цифры показывают, остается ли система здоровой, без заполнения хранилища бесконечными записями «200 OK».

Затем оставляйте небольшой сэмпл успешных запросов. Для стабильной функции часто достаточно 0,5%–1%. Такой срез дает инженерам реальные примеры заголовков, формы payload и времени выполнения, когда нужно сравнить здоровый случай со сломанным.

Новый код требует более внимательного наблюдения. Если команда только что поменяла auth, billing или фоновую задачу, временно поднимите долю сэмплирования для этого пути. 5% на неделю часто полезнее, чем 100% навсегда. Когда релиз стабилизируется и метрики остаются нормальными, снова снизьте уровень.

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

Для многих команд хорошо работает простой стандарт:

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

Такой подход дает покрытие без шума. Вы по-прежнему видите объем трафика, скорость и процент успеха по всей системе. У вас по-прежнему есть реальные успешные трассы, когда они нужны. Вы просто перестаете платить за хранение одного и того же здорового события снова и снова.

Сначала задайте правила, потом убирайте шум

Если сокращать логи без правил, команда рискует потерять ту единственную запись, которая нужна во время сбоя. Начните с тех сценариев, которые пользователи действительно замечают: регистрация, вход, оформление заказа, сброс пароля, биллинг, импорт и API-вызовы, связанные с деньгами или чувствительными данными.

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

Для каждого события в этих сценариях выберите один из трех уровней:

  • Полный лог, когда детали ошибки важны для отладки, аудита или поддержки
  • Сэмплированный лог, когда событие здоровое и очень частое
  • Только метрика, когда нужны лишь количество, задержка или процент ошибок

Сделайте правило простым. Неудачный платеж требует контекст запроса, деталей ошибки и trace IDs. Успешный health check, скорее всего, нуждается в счетчике и метрике задержки, а не в полном JSON каждые несколько секунд.

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

Практичная политика логирования приложений может выглядеть так: храните ошибки auth и billing 30–90 дней, сэмплированные логи успеха — 3–7 дней, а агрегированные метрики — намного дольше. Точные сроки зависят от нагрузки поддержки, требований compliance и бюджета.

Еще нужен понятный владелец. Если никто не отвечает за правила, любой может добавить шумные логи, а никто не будет их чистить. Назначьте одного инженера или тимлида, который будет утверждать изменения в логировании, и привлекайте продукт или поддержку к сценариям, влияющим на клиентов. Если первый вариант помогает настроить Fractional CTO, внутренний владелец все равно должен управлять ежедневными обновлениями.

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

Внедряйте по шагам

Начните с цифр. Возьмите объем логов за 7–30 дней по сервисам, окружениям и уровням логирования, а затем привяжите к каждой корзине реальные расходы. Команды часто спорят о логировании в теории. Счет быстро заканчивает этот спор и показывает, какой сервис создает больше всего повторяющегося шума об успехе.

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

Сначала измените один сервис, а не десять. Выберите самый шумный путь, который при этом кажется безопасным: health checks, фоновые задачи или сообщение об успешной регистрации, которое срабатывает весь день. Полные логи ошибок там оставьте. Для здоровых запросов храните небольшой сэмпл и только те поля, которые люди действительно ищут, например request ID, маршрут, задержку и статус-код.

Проверьте слепые зоны

После первого сокращения проверьте все, что зависело от этих успешных логов. Некоторые алерты читают количество успехов из логов вместо метрик. Некоторые дашборды ломаются, потому что строились на запросах к логам. Во время инцидента on-call-специалист может искать сообщение, которого больше нет. Исправьте эти пробелы, прежде чем расширять политику.

Короткая проверка после каждого изменения помогает держать сюрпризы маленькими:

  • Сравните объем логов до и после
  • Проверьте стоимость приема и хранения
  • Засеките время нескольких обычных поисков
  • Повторите недавний инцидент или проведите drill
  • Спросите у on-call-команды, что стало сложнее

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

Пример: поток регистрации

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

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

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

Для успешных регистраций храните только небольшой сэмпл. Можно сохранять 1 из каждых 100 успешных запросов с request ID и коротким описанием прошедших шагов. Этого достаточно, чтобы позже посмотреть реальные примеры, не оплачивая хранение каждого дубликата успеха.

Не прячьте объем в процессе. Считайте каждую успешную регистрацию в метриках, даже если не храните полную запись лога. На дашборде все равно должны быть общее число регистраций, процент ошибок валидации, сбои отправки email и задержка. Метрики показывают тенденции. Логи объясняют исключения.

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

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

Ошибки, которые допускают команды

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

Распространенная ошибка — удалять контекст вместе с повторяющимися сообщениями об успехе. Если лог ошибки говорит «платеж не прошел», но убирает путь запроса, tenant ID, код ответа, количество попыток и время, никто не сможет быстро разобраться. Вы немного экономите на хранении и теряете часы на разборе инцидента.

Команды также слишком далеко заходят с сэмплированием. Они оставляют лишь крошечный кусок обычного трафика, а потом удивляются, почему всплески задержки не видны в логах. Если один медленный запрос из 500 важен для пользователей, доля сэмплирования должна ловить его достаточно часто, чтобы был заметен паттерн.

Еще одна тихая проблема — срок хранения. Многие команды выбирают одно число, например 30 дней, и применяют его ко всему. Это выглядит аккуратно, но в реальности тратит деньги на малоценные записи и слишком рано удаляет историю ошибок. Логи ошибок, audit trail и сэмплированные логи успеха редко должны жить одинаково долго.

Debug output — еще один убийца бюджета. Команда включает его во время релиза, исправляет проблему и забывает выключить. Через две недели каждый запрос пишет лишние payload, заголовки и внутреннее состояние, которые никто не читает. Хранилище растет, поиск замедляется, а полезные сигналы тонут в шуме.

Поддержке становится сложнее, когда команды удаляют все доказательства успешных путей. Вам не нужны все успешные запросы навсегда, но обычно нужны хотя бы некоторые данные. Клиент говорит: «Я зарегистрировался, но письмо так и не пришло». Если вы сохранили небольшой сэмпл здоровых событий регистрации с message ID и timestamp, поддержка сможет проверить, что произошло, а не гадать.

Ранние признаки проблемы обычно такие:

  • Результаты поиска заполнены повторяющимися строками 200 OK
  • On-call-инженеры открывают логи и все равно не могут объяснить сбой
  • Хранилище быстро растет после каждого релиза
  • Поддержка просит у инженеров доступ к базе данных, чтобы ответить на базовые вопросы

Олег Сотников часто работает с командами, которые живут на компактной инфраструктуре, и здесь особенно важна дисциплина. Задача не в том, чтобы писать меньше логов. Задача в том, чтобы писать то, что помогает.

Быстрые проверки перед запуском

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

Перед тем как выкатить новую политику в production, прогоните короткое тестовое окно. Используйте реальные запросы из staging или небольшой кусок живого трафика, а затем смотрите на результаты критически.

  • Нарочно вызовите одну настоящую ошибку. Убедитесь, что логи показывают путь запроса, timestamp, имя сервиса, сообщение об ошибке и достаточно контекста, чтобы понять проблему пользователя только по логам.
  • Проведите один здоровый запрос через весь путь. Вам не нужен каждый успешный event, но вы должны все еще уметь проследить нормальный запрос между сервисами по одному request ID или trace ID.
  • Запустите несколько алертов в тестовом режиме. Если раньше они зависели от шумных успешных логов, после очистки они могут замолчать. Проверьте, что они по-прежнему срабатывают по проценту ошибок, задержке или неудачным заданиям.
  • Проверьте payload, которые вы оставили. Уберите токены, пароли, данные сессии, email-адреса и все, что не нужно читать специалисту поддержки.
  • Сравните стоимость до и после. Если расходы на хранение, прием или запросы не снизились в тестовом окне, значит, вы, скорее всего, оставили слишком много шума.

Один небольшой пример помогает. Допустим, регистрация не проходит, потому что сервис email не отвечает вовремя. Вы должны видеть попытку регистрации, timeout, результат повторной попытки и финальный ответ, отправленный пользователю. Для обычной регистрации обычно достаточно одной сэмплированной трассы и нескольких сводных метрик.

Здесь команды часто упускают суть. Они сокращают затраты на логирование, но одновременно убирают доказательства, нужные для отладки реальных проблем. Хорошая чистка убирает повторение, а не ясность.

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

Что делать дальше

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

Простой черновик часто работает лучше, чем идеальный. Если владелец сервиса может за минуту ответить на вопрос «мы это логируем, сэмплируем или убираем?», значит, политика работает.

  • Храните полные логи ошибок для неудачных запросов, исключений, повторных попыток, таймаутов и любых действий, связанных с деньгами, auth или изменением данных.
  • Сэмплируйте здоровые запросы для частых сценариев вроде входа, поиска и регистрации. Выберите низкую фиксированную долю и держите ее стабильной.
  • Установите более короткий срок хранения для шумных operational logs и более длинный — для audit или security-событий.
  • Запишите поля, которые должен хранить каждый сервис, например request ID, user или tenant ID, если это разрешено, имя сервиса, версию и время выполнения.

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

Задайте support и engineering один и тот же простой вопрос: «Какая недостающая деталь сильнее всего мешает вам, когда что-то ломается?» Их ответы обычно указывают на небольшой набор полей, которые важнее тысяч успешных строк. Поддержке могут понадобиться account IDs и timestamp. Инженерам — trace IDs, версия релиза и точная upstream-ошибка.

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

Если вашей команде нужен внешний взгляд, Oleg Sotnikov может посмотреть на шум в логах, инфраструктурные расходы и привычки команды в рамках консультации Fractional CTO. Такой разбор особенно полезен, когда расходы продолжают расти, а никто не может объяснить, какие логи все еще заслуживают своего места.