Регламент хранения логов: что писать, что сжимать, что удалять
«Логи занимают весь диск» и «в логах не нашлось нужной записи» — это две стороны одной и той же нерешённой задачи: на сервере нет явного регламента, что писать, что сжимать и что удалять. Без него команда обычно шарахается между крайностями — то включает максимальный уровень детализации везде и получает забитый диск, то в панике чистит всё подряд и остаётся без данных для расследования инцидента, который случится завтра. Разберём, как выстроить регламент хранения логов из трёх осмысленных фаз — запись, сжатие, удаление — и закрепить его конкретными конфигами logrotate, journald и Docker, а не устной договорённостью.
Содержание
Три фазы жизни лога, а не один рубильник
Типичная ошибка — воспринимать логирование как один переключатель: «логируем подробно» или «логируем скупо», «храним долго» или «чистим быстро». На деле у лога есть три разных этапа, и у каждого своя экономика риска.
- Запись — решение о том, что и с какой детализацией фиксируется прямо сейчас. Риск здесь двусторонний: слишком мало — и в момент инцидента нечего анализировать; слишком много — и полезный сигнал тонет в шуме, а диск и I/O тратятся на данные, которые никто никогда не откроет.
- Сжатие — что происходит с логом, когда он перестал быть «горячим», то есть нужным для чтения прямо сейчас, но ещё не устарел настолько, чтобы его выбросить. Здесь риск — забыть сжать вовремя и потерять место, либо сжать слишком рано и усложнить себе быстрый grep по вчерашнему инциденту.
- Удаление — момент, когда лог теряет ценность быстрее, чем растёт стоимость его хранения. Риск — удалить раньше, чем понадобилось для расследования, либо хранить бесконечно и платить диском (а иногда и соответствием требованиям по персональным данным) за то, что никто не читает.
Одно правило на все случаи — например «ротируем логи раз в неделю» — не устраивает никого: для журнала аутентификации неделя может быть слишком коротким сроком, а для подробного debug-лога веб-сервера — избыточно долгим. Регламент нужен, чтобы развести источники логов по трём фазам с разными параметрами для каждого.
Фаза записи: что логировать подробно, а что — нет
Первый и самый недооценённый рычаг экономии места — не ротация и не сжатие, а решение не писать лишнее с самого начала. Уровень детализации логирования должен зависеть от того, в проде вы или нет, и от того, что именно логируется.
Уровни логирования приложения. В production-окружении разумный уровень по умолчанию — info или warning, а не debug. Debug-уровень печатает служебные детали на каждый шаг обработки запроса и на нагруженном сервисе способен раздуть лог в десятки раз без единой полезной строки для типичного инцидента. Включать debug стоит точечно и временно — на время разбора конкретной проблемы, с явным планом вернуть уровень обратно, а не «просто на всякий случай пусть будет». Это управляется одной переменной окружения:
# .env продакшена
LOG_LEVEL=warning
# временно, только на время расследования
LOG_LEVEL=debug
Что не стоит писать целиком. Частые источники раздувания даже при разумном уровне логирования: полные тела HTTP-запросов и ответов, дампы SQL-запросов с параметрами, служебные health-check пинги от балансировщика, которые прилетают каждые несколько секунд без диагностической ценности. Отдельная опасность — попадание в лог токенов, паролей и персональных данных: это уже не трата места, а риск утечки. Подробнее, почему привычка «пишем всё подряд, вдруг пригодится» обходится дороже, чем кажется, — в статье про антипаттерн записи всего подряд на всякий случай.
Health-check запросы стоит исключать из access-лога веб-сервера явно, а не мириться с их постоянным присутствием. В nginx это делается через map:
# в http {}
map $request_uri $loggable {
/health 0;
/healthz 0;
default 1;
}
# в server {} или location {}
access_log /var/log/nginx/access.log combined if=$loggable;
Структурированный формат. Если приложение позволяет выбрать формат лога, JSON-логи (или хотя бы предсказуемый ключ=значение) окупаются на следующих двух фазах: по полю легко фильтровать при сжатии и находить нужный тип события при частичном удалении истории. Неструктурированный текст тоже годится, но требует больше ручной работы через grep/awk.
Разделение по назначению. Логи безопасности (аутентификация, sudo, fail2ban), логи доступа веб-сервера и отладочные логи приложения имеют разную ценность и разный профиль роста. Смешивать их в один файл — значит терять возможность настроить для каждого свою политику на следующих фазах. Уже на этапе записи разумно развести их по разным файлам или логгерам с разными путями.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверФаза сжатия: когда лог перестаёт быть горячим
Сжатие — не экономия места ради экономии, а разделение лога на «горячую» часть, которую читаете руками при разборе текущей ситуации, и «холодную», которая нужна редко и может лежать в компактном виде.
Сколько держать несжатым. Практичный ориентир — одни-двое суток в открытом виде: этого хватает, чтобы прогрепать вчерашний и сегодняшний лог без распаковки. В logrotate за это отвечает директива delaycompress: она откладывает сжатие свежеротированного файла на один цикл.
/var/log/myapp/*.log {
daily
rotate 30
compress
delaycompress
dateext
dateformat -%Y-%m-%d
missingok
notifempty
sharedscripts
postrotate
systemctl reload myapp >/dev/null 2>&1 || true
endscript
}
Директивы, которые чаще всего настраивают неверно или не настраивают вовсе: dateext вместе с dateformat -%Y-%m-%d даёт архивам понятные имена вида app.log-2026-08-25.gz вместо безликих app.log.1.gz — при разборе инцидента по дате это экономит время. sharedscripts заставляет postrotate-скрипт отработать один раз для всей группы файлов, а не на каждый отдельно. postrotate с перезагрузкой службы нужен, если приложение держит файл открытым по дескриптору и не подхватит новый само; для приложений без такой поддержки логичнее copytruncate.
Сжатие по объёму, а не только по времени. Для сервисов с неровной нагрузкой время — плохой триггер: за один пиковый день лог может вырасти на порядок больше обычного и не дождаться ночной ротации. Добавьте порог по размеру:
/var/log/myapp/access.log {
daily
rotate 14
maxsize 200M
compress
delaycompress
missingok
notifempty
}
maxsize заставляет logrotate ротировать файл, как только он превысит указанный размер, даже если по расписанию ещё не время — это защищает от исчерпания диска между плановыми прогонами cron.
Журнал systemd. journald сжимает записи автоматически (директива Compress=yes включена по умолчанию в /etc/systemd/journald.conf), поэтому отдельно настраивать сжатие для него обычно не требуется — важнее задать верхний предел размера, о чём в следующей фазе.
Насколько сжатие реально экономит место. Точный коэффициент зависит от структуры конкретного лога, поэтому конкретные проценты приводить не будем — это ориентир, который стоит проверить командой gzip -l после первой же ротации, а не считать универсальной константой. Текстовые логи с повторяющейся структурой обычно сжимаются заметно лучше хаотичных бинарных дампов, но точную цифру для вашего проекта покажет только замер.
Фаза удаления: где проходит граница разумного хранения
Удаление — самая спорная фаза регламента: здесь сталкиваются два противоположных интереса — чем дольше история логов, тем больше шансов найти причину проблемы, которая всплыла не сразу; чем короче — тем меньше диска и меньше юридических рисков, если в логах случайно оказались персональные данные.
Разные сроки для разных типов логов — не роскошь, а необходимость. Смешивать всё под одно число «храним 30 дней» так же неверно, как выбирать один уровень логирования для всех сервисов. Логи безопасности стоит хранить дольше остальных: инцидент со взломом или подбором пароля может обнаружиться далеко не в день события. Подробные access- и debug-логи можно чистить агрессивнее — их ценность резко падает уже через одну-две недели, если не идёт активное расследование.
Если проект обязан хранить определённые категории логов по требованию закона (например, логи доступа или VPN-подключений в некоторых юрисдикциях и отраслях), это отдельное ограничение поверх технической целесообразности — с ним стоит свериться прежде, чем настраивать автоматическую чистку. Требования по типам данных и срокам разобраны в статье о том, что нужно хранить в логах по закону, а общий подход к выбору срока хранения — в статье сколько хранить логи.
Механика удаления в logrotate. Директива rotate N определяет, сколько ротированных копий держать, прежде чем самая старая удаляется безвозвратно:
/var/log/myapp/security.log {
weekly
rotate 26
compress
delaycompress
missingok
notifempty
}
Здесь weekly + rotate 26 даёт примерно полгода истории для лога безопасности — заметно дольше, чем разумно держать подробный access-лог того же сервиса.
Файлы вне зоны ответственности logrotate. Не всё попадает под ротацию через штатные правила — старые архивы после смены формата логирования, экспортированные вручную дампы, файлы приложений, которые сами пишут архивы рядом с логом. Для них нужна отдельная, явная чистка через cron:
# /etc/cron.d/log-cleanup
0 4 * * * root find /var/log/archive -type f -name "*.gz" -mtime +180 -delete
Такое правило безвозвратно удаляет архивы старше 180 дней — важно проговорить этот срок в регламенте отдельной строкой, а не оставлять «где-то в cron», где его никто не пересматривает.
journald. Для журнала systemd срок хранения и предельный размер задаются вместе в /etc/systemd/journald.conf:
[Journal]
SystemMaxUse=1G
MaxRetentionSec=90day
SystemMaxUse ограничивает суммарный объём журнала на диске, MaxRetentionSec — предельный возраст записей: всё, что старше, journald удаляет сам при следующей ротации. Разовую принудительную чистку можно выполнить командой journalctl --vacuum-time=90d или journalctl --vacuum-size=1G.
Практические конфиги для типовых источников логов
Регламент должен закрывать не только собственное приложение, но и стандартные компоненты стека — веб-сервер, контейнеры, СУБД.
Docker. По умолчанию драйвер json-file не ограничивает размер лога контейнера, и это одна из самых частых причин внезапно закончившегося места на сервере с контейнерами. Ограничение задаётся в /etc/docker/daemon.json глобально для всех новых контейнеров:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "50m",
"max-file": "5"
}
}
После правки конфига нужно перезапустить демон (systemctl restart docker) — настройка применяется только к новым контейнерам, для уже запущенных нужно пересоздать их (docker compose up -d --force-recreate). Это даёт до 250 МБ на контейнер (5 файлов по 50 МБ) с автоматической ротацией без сжатия.
PostgreSQL. Встроенный сборщик логов ротирует файлы сам, но не удаляет старые — это остаётся на регламенте:
# postgresql.conf
logging_collector = on
log_rotation_age = 1d
log_rotation_size = 0
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
log_rotation_age = 1d ротирует раз в сутки, log_rotation_size = 0 отключает ротацию по объёму, оставляя только по времени (можно включить оба триггера одновременно, задав ненулевой размер). Поскольку сам Postgres не удаляет старые файлы, отдельной строкой в регламенте нужен cron:
0 3 * * * postgres find /var/log/postgresql -name "*.log" -mtime +30 -delete
Nginx через logrotate. В большинстве дистрибутивов базовое правило для nginx уже есть в /etc/logrotate.d/nginx, но его стоит сверить с общим регламентом проекта, а не оставлять значение по умолчанию без проверки — особенно rotate N, которое в дистрибутивном конфиге обычно рассчитано на общий случай, а не на объём трафика конкретного сайта.
Матрица регламента: сколько хранить и в каком виде
Свести регламент в одну таблицу — надёжный способ, чтобы он пережил конкретного инженера, который его придумал. Ориентир ниже стоит адаптировать под профиль проекта — это стартовая точка, а не догма.
| Тип лога | Уровень записи | Несжатый период | Сжатие | Срок хранения | Инструмент |
|---|---|---|---|---|---|
| Логи безопасности (auth, sudo, fail2ban) | Полная детализация всегда | 2 дня | gzip после несжатого периода | 90–180 дней | logrotate, weekly+rotate 26 |
| Access-лог веб-сервера | Полная детализация, без health-check запросов | 2 дня | gzip после несжатого периода | 14–30 дней | logrotate, daily+maxsize |
| Debug/отладочный лог приложения | warning в проде, debug только временно | 1 день | gzip сразу после ротации | 7–14 дней | logrotate, daily+rotate 14 |
| Журнал systemd (journald) | По умолчанию сервиса | — | встроенное, автоматически | По лимиту размера/возраста | SystemMaxUse, MaxRetentionSec |
| Логи контейнеров Docker | Как в приложении | — | не сжимаются штатно | По лимиту размера | max-size+max-file в daemon.json |
| Логи СУБД (PostgreSQL и подобные) | Ошибки и медленные запросы | 1 день | gzip внешним cron при необходимости | 30 дней | log_rotation_age + cron-удаление |
Столбец «Срок хранения» — техническая рекомендация, а не юридическое требование: если закон обязывает хранить конкретные категории логов дольше, приоритет за ним, а таблицу нужно скорректировать. Регламент стоит пересматривать не реже раза в год и сразу после любого инцидента, где не хватило исторических логов — это лучший сигнал, что сроки в таблице выбраны неверно. Общий еженедельный чек-лист обслуживания сервера, куда естественно встраивается ревизия логов, разобран в статье про еженедельный регламент обслуживания сервера.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
С чего начать, если сейчас на сервере вообще нет регламента логов?
С аудита: du -sh /var/log/* | sort -h, чтобы увидеть, что реально занимает место, и ls /etc/logrotate.d/, чтобы понять, что уже настроено штатно. Дальше — развести источники по таблице выше и закрыть дыры: обычно это отсутствие лимита у Docker и journald.
Можно ли просто удалить старые логи вручную одной командой и не настраивать регламент?
Разово — можно, это снимет симптом. Но без регламента диск заполнится снова через тот же срок, а ручная чистка рискует задеть логи, которые ещё нужны для текущего расследования — отсюда и нужен явный, повторяемый процесс.
Что если для расследования инцидента понадобился лог, который уже удалён по регламенту?
Значит, для этого типа лога срок хранения в таблице выбран слишком коротко — это повод пересмотреть регламент, а не разовое исключение. Если инцидент разбирается регулярно позже определённого срока, увеличьте rotate N именно для этой категории.
Нужно ли то же самое на одной небольшой VPS, или регламент оправдан только для больших инфраструктур?
На одной VPS регламент даже важнее: там некому напомнить, что диск заполняется, и разбираться с инцидентом придётся тому же человеку, который забыл настроить ротацию.
Как проверить конфиг logrotate перед тем, как довериться расписанию cron?
Тестовым прогоном без реальных изменений: logrotate -d /etc/logrotate.d/myapp — ключ -d показывает, что именно сделает logrotate, не трогая файлы. Принудительный реальный прогон — logrotate -f.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →