# logrotate: автоматическая ротация и архивация логов

Индекс LLMS: [llms.txt](/llms.txt)

---

Логи приложений забивают диск за неделю, а rm *.log вручную — путь к проблемам. logrotate решает это сам: ротирует, сжимает и удаляет старые файлы по расписанию. Разберёмся, как это работает и как настроить за пять минут.

## Как это устроено

logrotate вызывается через cron ежедневно. Конфиг по умолчанию живёт в `/etc/logrotate.conf`, а дополнительные конфиги подключаются из `/etc/logrotate.d/`. При ротации текущий файл переименовывается, создаётся новый пустой, старые копии сжимаются и нумеруются.

Цикл простой:

```
app.log        →  app.log.1      (сжатый: app.log.1.gz)
app.log.1.gz   →  app.log.2.gz
...
app.log.5.gz   →  удалён
```

Механизм работает через rename или mv, поэтому процесс должен держать дескриптор открытым. Если ротация не подхватывается приложением — получаете дублирование или пустой лог.

## Структура конфигурации

```bash
# /etc/logrotate.conf — глобальные настройки
weekly          # ротация раз в неделю
rotate 4        # хранить 4 копии
compress        # сжимать старые логи
include /etc/logrotate.d/
```

Файлы из `/etc/logrotate.d/` перекрывают глобальные значения для конкретных логов. Формат простой:

```
/путь/к/логу {
    директива значение
    ...
}
```

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

## Ключевые директивы

Базовые параметры, которые закрывают 90% задач:

| Директива | Назначение | Пример |
|-----------|------------|--------|
| `rotate N` | сколько копий хранить | `rotate 7` |
| `size N` | размер для триггера ротации | `size 100M` |
| `missingok` | не ошибка, если файла нет | — |
| `notifempty` | пропустить пустой лог | — |
| `compress` | сжимать старые (по умолчанию gzip) | — |
| `dateext` | дата вместо номера в имени | — |
| `dateformat` | формат даты | `dateformat -%Y%m%d` |
| `postrotate ... endscript` | команды после ротации | reload сервиса |
| `prerotate ... endscript` | команды до ротации | prepare директории |

`size` отменяет `weekly`/`monthly`/`daily`, если задан. Ротация происходит когда файл достигает указанного размера И прошёл интервал. То есть `size 100M` при `weekly` — ротация не раньше недели и если файл больше 100М.

> [!NOTE]
> `dateext` несовместим с длинными именами файлов на некоторых файловых системах. Если имя лога + суффикс даты выходит за 255 байт — logrotate упадёт.

## Пример конфига для приложения

Допустим, у нас сервис `myapp` пишет в `/var/log/myapp/`. Конфиг:

```bash
/var/log/myapp/*.log {
    daily
    rotate 14
    size 50M
    missingok
    notifempty
    compress
    dateext
    dateformat -%Y%m%d-%s
    sharedscripts
    postrotate
        systemctl reload myapp > /dev/null 2>&1 || true
    endscript
}
```

`sharedscripts` гарантирует один вызов `postrotate` для всех ротируемых файлов, а не для каждого. Без него скрипт выполняется столько раз, сколько файлов подпало под ротацию.

Для Python-приложений с ротацией через стандартный library:

```bash
/var/log/myapp/app.log {
    su root myapp
    daily
    rotate 7
    size 200M
    missingok
    notifempty
    compress
    postrotate
        /usr/bin/pkill -HUP -f "python.*myapp" || true
    endscript
}
```

`su` меняет владельца процесса ротации — полезно, если приложение запущено под отдельным пользователем и права на логи ограничены.

## Отладка и принудительный запуск

dry-run режим показывает что будет сделано без реальных действий:

```bash
logrotate -d /etc/logrotate.d/myapp
```

Вывод содержит каждое решение: какой файл переименовывается, какой сжимается, какие команды выполняются. Смотрите на строки `renaming` и `running postrotate script`.

Принудительная ротация минуя расписание:

```bash
logrotate -f /etc/logrotate.d/myapp
```

`-f` игнорирует время последней ротации и размеровые условия. Комбинация с `-d` — безопасный способ проверить перед продакшеном:

```bash
logrotate -d -f /etc/logrotate.d/myapp
```

Если нужно ротировать конкретный лог вне расписания, но с учётом условий — используйте state-файл:

```bash
# посмотреть состояние
cat /var/lib/logrotate/status

# временно сдвинуть время последней ротации
sed -i 's|/var/log/myapp/app.log.*|/var/log/myapp/app.log 2024-01-01-00:00:00|' /var/lib/logrotate/status
logrotate /etc/logrotate.d/myapp
```

Проверка синтаксиса без выполнения:

```bash
logrotate -d /etc/logrotate.conf
```

Если ошибок нет — вывод пустой (без `-d`) или показывает план действий (с `-d`).

> [!WARNING]
> Не редактируйте `/var/lib/logrotate/status` вручную в продакшене без понимания формата. Одна ошибка — и logrotate решит что ротация уже прошла, пропустит все файлы до следующего запуска cron.

Настройка cron по умолчанию:

```bash
# /etc/cron.daily/logrotate
#!/bin/sh
test -x /usr/sbin/logrotate || exit 0
/usr/sbin/logrotate /etc/logrotate.conf
```

В большинстве дистрибутивов трогать этот файл не нужно. Если нужен запуск чаще раза в сутки — добавляйте в `/etc/cron.hourly/` или пишите отдельный cron job.
