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

Лог должен отвечать на вопрос, а не просто фиксировать факт
Запись «ошибка сервера» почти бесполезна: в ней нет ни операции, ни пользователя, ни идентификатора запроса, ни причины сбоя. Но и полный дамп каждого входящего запроса не спасает систему. Он увеличивает расходы на хранение, замедляет обработку и может вынести в лог пароль, токен или персональные данные.
Ниже — практический подход для production-приложений, которые пишут структурированные логи и отправляют их в централизованное хранилище. Ориентир по терминологии — OpenTelemetry Specification 1.x; конкретные имена полей зависят от языка и библиотеки, поэтому их нужно сверять с документацией вашего стека.
Лог — это не копия программы в текстовом виде. Это доказательство того, что произошло, когда произошло и в каком контексте.
Какие события стоит писать
Сначала определите события, по которым инженер будет восстанавливать цепочку действий. Для каждого события должны быть понятны время, место возникновения и результат операции.
Минимальный набор полей
- Время — в UTC и с точностью, достаточной для сопоставления событий между сервисами.
- Уровень — например, debug, info, warn или error. Уровень должен отражать последствия, а не настроение автора записи.
- Сообщение события — стабильное описание вроде «платёж отклонён», а не динамическая строка с десятками значений.
- Сервис и версия — имя компонента, версия приложения, окружение и при необходимости регион или зона.
- Идентификатор корреляции — trace_id, request_id или другой идентификатор, по которому можно найти соседние записи.
- Результат операции — статус, код ошибки, длительность и безопасные параметры, которые помогают понять исход.
Структурированный лог хранит эти значения отдельными полями. Если положить всё в одну строку, поиск по статусу или длительности будет зависеть от формулировки сообщения и версии шаблона.
События, которые обычно нужны при разборе
Логируйте не каждую строку кода, а переходы, важные для бизнеса и эксплуатации:
- входящий запрос и его завершение — с маршрутом, методом, кодом ответа и длительностью;
- попытку аутентификации — с результатом и причиной отказа без пароля и токена;
- создание, изменение и удаление сущностей — с типом объекта, его внутренним идентификатором и инициатором;
- вызов внешнего сервиса — с названием зависимости, результатом, временем ожидания и числом повторов;
- изменение конфигурации, прав доступа или feature flag — кто изменил, что именно и какой результат получен;
- запуск фоновой задачи — с идентификатором задания, количеством обработанных объектов и финальным статусом;
- исключение или отказ — с типом ошибки, безопасным stack trace и контекстом операции.
Для финансовой операции полезнее событие «резервирование средств отклонено внешним провайдером» с кодом ответа и длительностью, чем запись «не удалось обработать заказ». Первая запись позволяет искать причину, вторая только сообщает о симптоме.
Что не нужно писать в лог
Запрет касается не только очевидных паролей. Любое значение, с помощью которого можно войти в систему, восстановить секрет или связать действие с конкретным человеком, требует отдельного решения: не писать, маскировать, хешировать или ограничить доступ.
| Данные | Почему опасны | Что писать вместо этого |
|---|---|---|
| Пароли, API-ключи, access- и refresh-токены | По записи можно получить доступ к системе | Тип операции, идентификатор клиента, результат; секрет не сохранять |
| Полный заголовок Authorization и cookies | В них часто находятся учётные данные сессии | Признак наличия авторизации и безопасный идентификатор сессии |
| Номера карт, документы, медицинские сведения | Риск утечки и нарушение требований к обработке данных | Тип платёжного метода, маскированное значение или внутренний ID |
| Полное тело запроса | В нём могут оказаться пароли, персональные данные и большие файлы | Размер, схема операции, список безопасных полей и результат валидации |
| SQL-запросы с параметрами пользователей | Можно раскрыть данные и получить огромный объём шума | Имя операции, длительность, число строк и факт ошибки |
Маскирование — не универсальная защита. Если система заменяет несколько символов, но оставляет достаточно данных для восстановления исходного значения или сопоставления с другой утечкой, риск остаётся. Для чувствительных полей безопаснее не логировать значение вообще.
Уровни: ошибка — это не всё, что пошло не по плану
Уровень определяет реакцию дежурного и стоимость хранения. Если ставить error на каждое ожидаемое отклонение, алерт перестанет отличать инцидент от обычного отказа пользователя.
| Уровень | Когда использовать | Пример |
|---|---|---|
| debug | Подробности для локальной диагностики или временного расследования | Промежуточное решение алгоритма с безопасными параметрами |
| info | Нормально завершившееся значимое событие | Фоновая задача обработала пакет объектов |
| warn | Отклонение, которое система пережила, но его стоит проверить | Внешний сервис ответил после повторной попытки |
| error | Операция не выполнена или сервис не может гарантировать корректный результат | Транзакция откатилась из-за недоступности зависимости |
Не используйте уровень как замену классификации. Событие может быть info для одного сервиса и error для другого: важен эффект именно на текущую операцию. Отдельно задайте правила для алертов. Один error не всегда означает инцидент, а сотня warn за минуту может указывать на деградацию.
Контекст: сколько его достаточно
Контекст помогает связать запись с запросом, пользователем и зависимостью, но не должен превращать лог в снимок всей памяти процесса. Добавляйте только те поля, которые отвечают на конкретный вопрос расследования.
- Для HTTP-запроса: метод, нормализованный маршрут без секретов в URL, код ответа, длительность и request_id.
- Для фоновой задачи: имя задачи, идентификатор запуска, размер пакета, число успехов и ошибок.
- Для интеграции: имя внешней системы, операция, тайм-аут, число повторов и классификация ответа.
- Для доступа: субъект, действие, объект, результат и причина отказа.
Не кладите в контекст объект запроса целиком. Веб-фреймворк может добавить туда заголовки, cookies и тело. Лучше собрать разрешённый набор полей явно и применить единый фильтр удаления чувствительных значений до отправки записи в хранилище.
Корреляция — способ связать события одной операции между несколькими компонентами. Обычно для этого используют request_id для одного входящего запроса и trace_id для распределённой цепочки вызовов.[1]
Типичные ошибки, которые дорого обходятся
Логирование на каждый проход цикла
Так появляются миллионы почти одинаковых записей и нагрузка на диск, агент сбора и хранилище. Для пакетной обработки обычно достаточно записи о начале, завершении, длительности, размере пакета и агрегированных ошибках. Детали отдельных объектов включайте только при диагностическом режиме и с ограничением объёма.
Разные названия одного события
«Payment failed», «payment_error» и «Ошибка оплаты» затрудняют поиск и дешборды. Зафиксируйте имена событий, обязательные поля и допустимые значения. Шаблон сообщения должен оставаться стабильным, а переменные параметры — уходить в отдельные поля.
Stack trace без идентификатора операции
Трассировка исключения без request_id часто не связывается с запросом и логами соседних сервисов. Сохраняйте тип ошибки и стек там, где это разрешено политикой безопасности, но обязательно добавляйте контекст операции.
Секреты появляются после нового релиза
Фильтр может закрывать только известные ключи: token и password. Новый параметр вроде session_secret или payment_data обойдёт его. Защитите несколько уровней: не передавайте секреты в логирующий вызов, применяйте allowlist полей и проверяйте тестами реальные записи после изменений.
Как проверить качество логирования
Проверяйте не количество строк, а способность ответить на эксплуатационные вопросы. Для пяти последних ошибок попробуйте восстановить:
- какая операция началась и где завершилась;
- к какому запросу или запуску задачи она относится;
- какой компонент и какая версия участвовали;
- какая зависимость отказала и сколько времени занял вызов;
- что увидел пользователь и была ли операция повторена;
- есть ли в записях секреты, персональные данные или полный входной документ.
Если на один из вопросов нет ответа, добавьте конкретное поле или событие. Если ответ есть только после просмотра десяти разных шаблонов, унифицируйте схему. Если ответ требует раскрыть весь payload, пересмотрите границы допустимого контекста.
Где подход ломается
Логи не заменяют метрики и трассировку. По логам удобно разбирать конкретную операцию, но они хуже показывают долю ошибок, распределение задержек и насыщение ресурсов. Для таких вопросов нужны метрики. При цепочке из нескольких сервисов одних request_id может быть недостаточно — нужна распределённая трассировка и согласованная передача контекста.
Есть и технические границы: запись может потеряться при аварийном завершении процесса, события могут прийти в хранилище не по порядку, а повторная отправка создаст дубликаты. Не стройте критическую бизнес-логику на факте появления строки в логе. Если событие важно для аудита или финансового учёта, храните его в предназначенном для этого надёжном журнале или базе.
Что делать дальше
Выберите один критичный пользовательский сценарий и составьте для него карту событий: вход, ключевые действия, внешние вызовы, результат и ошибка. Затем проверьте записи в тестовом окружении с реальными отказами — тайм-аутом, недоступной базой и неверным ответом зависимости. После этого настройте срок хранения, доступ к логам, маскирование и лимит объёма.
В production следите как минимум за объёмом логов на запрос, долей записей с ошибками, потерями при доставке и стоимостью хранения. Следующий шаг — связать эти показатели с трассировкой и метриками, а не добавлять ещё больше строк.
Поиск источников: Реальные статьи и книги со ссылками, библиография по ГОСТ
Спросить за 15 ₽Источники: OpenTelemetry Documentation: Logs, OWASP Logging Cheat Sheet
Частые вопросы
Нужно ли логировать каждый HTTP-запрос?
Можно ли писать в лог email или внутренний ID пользователя?
Что делать, если в production уже попал токен?
Нужны ли stack trace в production?
Что делать, если у меня другая библиотека или формат логов?
Почему логи есть в приложении, но их нет в системе мониторинга?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.