Логи системы: journalctl, rsyslog, logrotate

Где на самом деле лежат логи современного Linux, как искать в бинарном журнале systemd и как не дать логам забить диск.

journald — компонент systemd, который собирает структурированные логи всех служб в бинарный журнал; читают его командой journalctl, а не cat.

Зачем это на практике

«Сервис не работает» — и первое, что делает админ, открывает логи. На systemd-системах журнал у каждой службы общий и индексированный: можно фильтровать по юниту, времени, приоритету и даже по PID — без grep по гигабайтам. Параллельно существует классический rsyslog с текстовыми файлами в /var/log, и оба надо уметь читать. А logrotate спасает диск от переполнения.

journalctl: основные фильтры

Без аргументов journalctl вываливает весь журнал от старта системы. На практике всегда фильтруют:

journalctl -u nginx.service          # только логи юнита nginx
journalctl -u nginx -f                # следить в реальном времени (как tail -f)
journalctl -u nginx --since "10 min ago"
journalctl --since "2024-01-01" --until "2024-01-02 12:00"
journalctl -b                         # логи только текущей загрузки
journalctl -b -1                      # предыдущей загрузки (искать причину падения)
journalctl -k                         # сообщения ядра (= dmesg)

Флаг -u (unit) — самый частый. -f (follow) и -b (boot) экономят часы.

Приоритеты (severity)

Каждая запись имеет уровень от 0 (emerg) до 7 (debug). Фильтр -p показывает выбранный уровень и важнее:

КодУровеньКогда
0–3emerg/alert/crit/errчто-то сломалось
4warningподозрительно
6infoобычные события
7debugдетальная отладка
journalctl -p err -b                  # только ошибки и хуже за эту загрузку
journalctl -u myapp -p warning --since today
journalctl -u myapp -o json-pretty    # структурированный вывод со всеми полями

Постоянный журнал

По умолчанию во многих дистрибутивах журнал хранится в /run/log/journal — это tmpfs в памяти, и после перезагрузки он исчезает. Чтобы журнал переживал ребут (критично для разбора падений), его делают постоянным:

sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
# или явно в конфиге:
# /etc/systemd/journald.conf  ->  [Journal]\nStorage=persistent
sudo systemctl restart systemd-journald

journalctl --disk-usage               # сколько занимает журнал
sudo journalctl --vacuum-size=500M    # обрезать до 500 МБ
sudo journalctl --vacuum-time=2weeks  # удалить старше 2 недель

/var/log и rsyslog

Даже с journald многие демоны и сам rsyslog пишут текстовые файлы в /var/log. Знать их наизусть полезно:

/var/log/syslog или /var/log/messagesобщесистемные сообщения
/var/log/auth.log или /var/log/secureаутентификация, sudo, ssh-входы
/var/log/kern.logсообщения ядра
/var/log/nginx/, /var/log/dpkg.logлоги конкретных служб/пакетов

rsyslog принимает сообщения и раскладывает по файлам согласно правилам facility.priority. Конфиг — /etc/rsyslog.conf и /etc/rsyslog.d/*.conf:

# /etc/rsyslog.d/50-myapp.conf — все сообщения от local0 в отдельный файл
local0.*    /var/log/myapp.log

# проверить конфиг и перезапустить
sudo rsyslogd -N1
sudo systemctl restart rsyslog

# отправить тестовую запись
logger -p local0.info "проверка связи"

Ротация: logrotate

Текстовые логи растут бесконечно и забивают диск. logrotate по расписанию (обычно через systemd-timer или cron) переименовывает, сжимает и удаляет старые файлы. Правила — в /etc/logrotate.d/:

# /etc/logrotate.d/myapp
/var/log/myapp.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    copytruncate
}

Что значат ключи: daily — раз в сутки; rotate 14 — хранить 14 архивов; compress — gzip старых; missingok — не ругаться, если файла нет; copytruncate — копировать и обнулить файл (для демонов, которые держат файл открытым и не умеют переоткрывать его по сигналу).

sudo logrotate -d /etc/logrotate.d/myapp   # -d = dry-run, ничего не меняет, показывает план
sudo logrotate -f /etc/logrotate.d/myapp   # -f = принудительно прокрутить сейчас

Как это работает под капотом

journald получает логи несколькими путями: stdout/stderr каждой службы перехватывается systemd и пишется в журнал автоматически (поэтому print() в вашем приложении сразу виден в journalctl -u); плюс есть нативный сокет и приём от syslog(). Журнал хранится в индексированных бинарных файлах .journal с полями-ключами (_SYSTEMD_UNIT, _PID, PRIORITY), поэтому фильтрация по юниту/времени — это поиск по индексу, а не перебор строк. Записи криптографически последовательны, и journald отслеживает повреждение файлов. rsyslog и journald часто работают вместе: journald — первичный приёмник, а rsyslog читает из него и пишет привычные текстовые файлы для совместимости и пересылки на удалённый лог-сервер.

Частые ошибки

  • Ищут логи службы в /var/log/myapp.log, а она пишет в stdout → всё в journalctl -u myapp, файл пуст.
  • Журнал не постоянный: после ребута логи падения исчезли, потому что Storage остался в памяти.
  • Диск забился логами журнала — забыли про SystemMaxUse в journald.conf или про --vacuum.
  • logrotate с обычным переименованием для демона, который держит дескриптор открытым: служба продолжает писать в «старый» файл. Решение — copytruncate или сигнал на переоткрытие через postrotate.
  • Забыли journalctl -b -1 при разборе внезапной перезагрузки и смотрят только текущую загрузку, где причины уже нет.

Итоги

  • journalctl -u ИМЯ -f и -p err — рабочие лошадки разбора инцидентов.
  • -b/-b -1 переключают между текущей и прошлой загрузкой.
  • Постоянный журнал (/var/log/journal) нужен, чтобы логи переживали перезагрузку.
  • /var/log/auth.log, syslog, kern.log — классические текстовые логи rsyslog.
  • logrotate не даёт логам забить диск: rotate, compress, copytruncate.
Проверьте себя
1. Сервер внезапно перезагрузился. Какой командой посмотреть логи именно ПРЕДЫДУЩЕЙ загрузки, чтобы найти причину?
Ajournalctl -f
Bjournalctl -b -1
Cjournalctl --since today
Djournalctl -u kernel
2. Демон держит лог-файл открытым и не умеет переоткрывать его по сигналу. Какой ключ logrotate позволит ротировать его без потери записей?
Acompress
Bmissingok
Ccopytruncate
Dnotifempty