В этом уроке разберем journalctl с нуля и до боевого применения: как просто посмотреть логи Linux, как фильтровать по сервису, по времени и по ошибкам, как читать вывод и понимать, что в нем норма, а что тревога, как раскрутить причину сбоя по цепочке. В конце - шпаргалка частых команд, мини-лаба и контрольные вопросы. Все актуально на 2026 год: systemd сейчас стоит почти везде (systemd 256/257 в свежих дистрибутивах), и на отечественных Astra Linux и RED OS под капотом тот же systemd-journald.
Что такое журнал systemd и чем он отличается от обычных логов linux
В классической схеме каждый демон писал свой текстовый файл: /var/log/syslog, /var/log/auth.log, /var/log/nginx/error.log и так далее. Это работает, но неудобно: чтобы соединить события по времени из разных файлов, приходится прыгать туда-сюда и руками сводить таймстемпы.
Systemd собирает все в один структурированный двоичный журнал. Демон systemd-journald ловит сообщения от ядра, от сервисов (их stdout/stderr), от системного лога (syslog), от библиотеки журналирования и складывает их вместе с метаданными. И это ключевое отличие: каждая запись - это не строка текста, а набор полей "ключ=значение". Кроме самого сообщения MESSAGE там лежат PRIORITY (уровень важности), _SYSTEMD_UNIT (какой юнит написал), _PID, _UID, _COMM (имя бинарника), _HOSTNAME, _BOOT_ID (идентификатор загрузки) и десятки других. Полный набор полей описан в man systemd.journal-fields.
Двоичный структурированный формат - это не каприз: благодаря ему ты можешь мгновенно фильтровать миллионы строк по полю, например "покажи только сообщения от юнита nginx с уровнем error за последний час". Текстовым grep так быстро и точно не сделаешь. Чтобы увидеть все поля записи целиком, добавь -o verbose:
Код: Выделить всё
journalctl -u ssh -n 1 -o verbose
Базовый просмотр: листаем, следим, читаем с конца
Самая простая команда - без аргументов:
Код: Выделить всё
journalctlКод: Выделить всё
Jun 15 10:42:13 web01 sshd[1287]: Accepted password for deploy from 10.0.0.5 port 51234 ssh2- Jun 15 10:42:13 - время события. Хочешь точные таймстемпы с годом и часовым поясом - добавь -o short-iso или -o short-precise (будет видна и доля секунды, что важно при анализе всплесков).
- web01 - имя хоста.
- sshd - имя процесса (_COMM), который написал строку. Внимание: это имя бинарника, а не обязательно имя юнита. Юнит лежит в поле _SYSTEMD_UNIT и может называться иначе (например ssh.service против бинарника sshd).
- 1287 - PID, номер процесса. По нему потом можно отфильтровать.
- дальше - само сообщение (MESSAGE).
Код: Выделить всё
journalctl -e # сразу прыгнуть в КОНЕЦ журнала (самое свежее)
journalctl -f # следить за новыми строками в реальном времени, как tail -f
journalctl -r # вывести в ОБРАТНОМ порядке, новые сверхуЧтобы не утонуть в пейджере, ограничивай число строк, а для скриптов отключай пейджер:
Код: Выделить всё
journalctl -n 50 # последние 50 строк
journalctl -u nginx -n 100
journalctl -u nginx --no-pager # без less, удобно в скриптах и пайпахВся сила journalctl - в фильтрах. Их можно комбинировать, и тогда выборка становится точечной.
По сервису (юниту). Это первое, что делаешь, когда знаешь, какой компонент сломан. Связка journalctl service - самый ходовой сценарий:
Код: Выделить всё
journalctl -u nginx.service
journalctl -u ssh -f # следим за логами SSH вживую
journalctl -u nginx -u php8.3-fpm # сразу несколько юнитов (логика ИЛИ)Код: Выделить всё
journalctl --user -u myapp.serviceКод: Выделить всё
journalctl -p err # ошибки и хуже
journalctl -p warning -b # предупреждения и выше за текущую загрузку
journalctl -u nginx -p err # только ошибки конкретного сервиса
journalctl -p 0..3 # диапазон: от emerg до err- 0 emerg, 1 alert, 2 crit - система реально в беде.
- 3 err - ошибки. С этого уровня обычно и начинают расследование.
- 4 warning - предупреждения, часто безобидные, но иногда подсказка.
- 5 notice, 6 info, 7 debug - обычная болтовня и отладка.
По времени. Чтобы не искать иголку в стоге, ограничивай период. Запрос journalctl since закрывает большинство случаев:
Код: Выделить всё
journalctl --since "2026-06-15 10:00:00"
journalctl --since "1 hour ago"
journalctl --since today
journalctl --since "09:00" --until "10:30"
journalctl -u nginx --since "30 min ago" -p errПо загрузке системы. Каждая загрузка (boot) имеет свой _BOOT_ID. Это спасает после ребута:
Код: Выделить всё
journalctl -b # логи ТЕКУЩЕЙ загрузки
journalctl -b -1 # логи ПРЕДЫДУЩЕЙ загрузки (что было до ребута)
journalctl --list-boots # список всех загрузок со смещениямиТолько ядро. Сообщения ядра (аналог dmesg), сюда падают проблемы с железом, диском, сетью на низком уровне, OOM-киллер:
Код: Выделить всё
journalctl -k # ядро за текущую загрузку
journalctl -k -p err -b # ошибки ядра
journalctl -k -g -i "killed process" # ловим жертв OOM-киллераКод: Выделить всё
journalctl _PID=1287
journalctl _SYSTEMD_UNIT=nginx.service _UID=33 # несколько полей = логика И
journalctl -u nginx | grep -i timeout
journalctl -g "out of memory" # встроенный grep по шаблону
journalctl -g "timeout" -i # -i делает поиск регистронезависимымОбъяснение ошибок. Флаг -x подмешивает к известным сообщениям подсказку из каталога (что это значит и куда копать). Подсказки есть не для всех записей, но для системных событий часто выручают:
Код: Выделить всё
journalctl -xe # хвост журнала с пояснениями к ошибкамjournalctl умеет отдавать структуру, а не только текст - это пригодится, когда логи уезжают в мониторинг:
Код: Выделить всё
journalctl -u nginx -o json-pretty # все поля записи в JSON
journalctl -o json | jq .MESSAGE # дальше парсим через jqПостоянное хранение и размер журнала
Тут кроется грабля, на которую напарывается почти каждый новичок. Параметр Storage в /etc/systemd/journald.conf по умолчанию равен auto, и ведет себя так: если каталог /var/log/journal существует - пишем на диск (persistent), если нет - держим журнал в /run/log/journal, то есть в оперативной памяти (volatile), и после перезагрузки он обнуляется. На многих установках Debian и Ubuntu каталог /var/log/journal по умолчанию не создается, поэтому де-факто журнал живет в RAM. Вот почему journalctl -b -1 нередко отвечает "нет данных" - логов прошлой загрузки просто не сохранилось. На RHEL/Fedora и ряде других каталог обычно уже есть, и хранение постоянное.
Чтобы гарантированно включить постоянное хранение, есть два пути. Простой - просто создать каталог (при Storage=auto этого достаточно). Явный и надежный - прописать persistent:
Код: Выделить всё
sudo mkdir -p /var/log/journal
# вариант с явной настройкой в /etc/systemd/journald.conf:
# [Journal]
# Storage=persistent
sudo systemctl restart systemd-journaldКод: Выделить всё
journalctl --disk-usageКод: Выделить всё
sudo journalctl --vacuum-size=500M # ужать до 500 МБ
sudo journalctl --vacuum-time=2weeks # удалить старше 2 недель
sudo journalctl --vacuum-files=10 # оставить максимум 10 файлов журналаТипичные грабли и заблуждения
- "journalctl -b -1 пустой - значит логов нет". Нет, скорее всего у тебя журнал в RAM (Storage=auto без каталога /var/log/journal). Настрой постоянное хранение заранее, ДО того как что-то сломается, иначе причину ночного ребута ты не увидишь.
- "Логов нет вообще". Проверь права: обычный пользователь видит свой user-журнал, но не системный. Добавь себя в группу systemd-journal (или adm), перелогинься, либо запускай через sudo.
- Путаница -e и -r. -e просто прыгает в конец (порядок прямой), -r переворачивает вывод (новые сверху). Это разные вещи.
- Путаница _COMM и _SYSTEMD_UNIT. В строке видно имя бинарника (sshd), а -u ждет имя юнита (ssh.service). Если -u ничего не дал, проверь реальное имя через journalctl -F _SYSTEMD_UNIT.
- "journalctl показывает не все, что писал сервис". Если приложение пишет в собственный файл (например nginx в /var/log/nginx/error.log напрямую), в журнал попадет только то, что ушло в stdout/stderr. Смотри и журнал, и файлы приложения.
- Часовой пояс. --since считается в локальном времени машины. На сервере в UTC легко промахнуться на несколько часов - сверяйся через -o short-iso.
- Забывают про -b. Без него фильтры по времени могут зацепить старые загрузки. Привыкай добавлять -b, когда расследуешь свежий инцидент.
- Выполни journalctl -n 20 и разбери одну строку по полям: время, хост, процесс, PID. Затем journalctl -n 1 -o verbose и найди в нем _SYSTEMD_UNIT и PRIORITY.
- Запусти journalctl -f, в другом окне сделай sudo systemctl restart ssh и поймай событие в реальном времени.
- Покажи только ошибки текущей загрузки: journalctl -p err -b. Пусто? Подними планку: journalctl -p warning -b. Есть ли что-то тревожное?
- Посмотри логи одного сервиса за час: journalctl -u ssh --since "1 hour ago".
- Найди жертв OOM, если они были: journalctl -k -g -i "killed process".
- Проверь размер журнала: journalctl --disk-usage. Загляни в /etc/systemd/journald.conf, найди строку Storage и проверь, существует ли /var/log/journal.
Код: Выделить всё
journalctl -e # в конец журнала
journalctl -f # следить вживую (tail -f)
journalctl -n 50 # последние 50 строк
journalctl -r # новые сверху
journalctl -u nginx # логи сервиса
journalctl -u nginx -f # следить за сервисом
journalctl --user -u myapp # логи пользовательского сервиса
journalctl -p err -b # ошибки текущей загрузки
journalctl -p 0..3 # диапазон приоритетов
journalctl --since "1 hour ago" # за последний час
journalctl --since today --until "10:30"
journalctl -b # текущая загрузка
journalctl -b -1 # предыдущая загрузка
journalctl -k # сообщения ядра
journalctl _PID=1287 # по процессу
journalctl -g "out of memory" -i # встроенный grep (PCRE), без регистра
journalctl -xe # хвост с пояснениями к ошибкам
journalctl -o json-pretty # все поля в JSON
journalctl --disk-usage # размер журнала
sudo journalctl --vacuum-size=500M # чистка
journalctl --verify # проверка целостности- Чем отличаются флаги -e, -f и -r? Когда какой удобнее?
- Как показать только ошибки конкретного сервиса за текущую загрузку? Напиши команду.
- Почему journalctl -b -1 может вернуть пусто и как это исправить заранее (два способа)?
- В чем разница между _COMM и _SYSTEMD_UNIT и почему -u иногда "ничего не находит"?
- Какой командой узнать размер журнала и как ужать его до 1 ГБ?
journalctl - это окно в журнал systemd, где каждая запись хранится как набор полей, а вся работа сводится к фильтрам. Базис: -e в конец, -f следить, -n ограничить. Расследование: -u сервис, -p err по ошибкам (помни, что приоритет ставит само приложение), --since по времени, -b по загрузке, -k по ядру, -g для поиска по PCRE, -x за пояснениями. Чтобы логи переживали ребут, проверь Storage и наличие /var/log/journal, а размер держи под контролем через --disk-usage и vacuum. Для централизации в 2026 отдавай -o json в Vector/Alloy/Fluent Bit и Loki или ELK. Освоив эту шпаргалку, ты сможешь посмотреть логи Linux и найти причину почти любого сбоя, не открывая руками ни одного файла.