Зачем единый журнал
До systemd каждый сервис писал логи по-своему: nginx в /var/log/nginx/access.log, postgres в /var/log/postgresql/postgresql-16-main.log, ssh в /var/log/auth.log через syslog, своё приложение — куда придумал автор. Чтобы понять «что произошло на сервере за последний час», нужно было tail десяток файлов с разным форматом.
systemd-journald решает это, собирая логи всех units (и не только) в structured journal с metadata. Команда journalctl — это твоё окно в этот журнал.
Структурированность важна: каждая запись имеет fields (UNIT=nginx.service, _PID=12347, PRIORITY=3, _UID=33, MESSAGE=...). Это позволяет фильтровать «все ошибки от nginx с PID 12347 между 14:00 и 14:15» одной командой.
journalctl без аргументов: «весь журнал»
$ journalctl
-- Logs begin at Mon 2026-05-12 03:00:00 UTC, end at Wed 2026-05-13 11:45:31 UTC. --
May 12 03:00:01 prod-vm systemd[1]: Started Daily apt download.
May 12 03:00:01 prod-vm CRON[12345]: pam_unix(cron:session): session opened for user root
May 12 03:00:02 prod-vm postfix/master[12347]: starting Postfix mail system
...
По умолчанию open pager (less). Выход — q. Внутри: / поиск, g/G начало/конец, n/N next match.
Без sudo ты увидишь логи своего user и общие. С sudo — логи всех users и системных units (в большинстве случаев). Точные права зависят от группы systemd-journal — если ты в ней, читаешь всё без sudo.
$ groups
levo sudo docker systemd-journal
$ journalctl # читает всё без sudo
journalctl -u SERVICE: один сервис
Самая частая команда:
$ journalctl -u nginx
May 13 08:42:11 prod-vm systemd[1]: Starting nginx.service - A high performance web server...
May 13 08:42:11 prod-vm nginx[12346]: nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
May 13 08:42:11 prod-vm nginx[12346]: nginx: configuration file /etc/nginx/nginx.conf test is successful
May 13 08:42:11 prod-vm systemd[1]: Started nginx.service - A high performance web server...
Все логи только от nginx.service. Можно несколько unit-ов через несколько -u:
$ journalctl -u airflow-scheduler -u airflow-celery-worker
Это даст merged-feed обоих сервисов — полезно для дебага, когда ошибка касается нескольких компонентов.
-n N: last N lines
$ journalctl -u nginx -n 50
Последние 50 строк. По умолчанию -n без числа = 10. Аналог tail -n.
-f: follow live
$ journalctl -u nginx -f
Стримит логи в реальном времени, как tail -f. Останов: Ctrl+C. Можно совместить с -u, -p, и т.д.
Самый частый workflow дебага:
# Терминал 1: live-tail
$ journalctl -u my-etl -f
# Терминал 2: запустить ETL и смотреть в первом терминале как идёт
$ sudo systemctl restart my-etl
—since и —until: по времени
$ journalctl -u nginx --since today
$ journalctl -u nginx --since "1 hour ago"
$ journalctl -u nginx --since "2026-05-13 09:00"
$ journalctl -u nginx --since "2026-05-13 09:00" --until "2026-05-13 10:00"
$ journalctl -u nginx --since yesterday --until "yesterday 23:59"
Форматы времени:
today,yesterday,now— относительно."1 hour ago","30 min ago","2 days ago"— relative."YYYY-MM-DD"— конкретная дата (всё за этот день)."YYYY-MM-DD HH:MM:SS"— момент.
# Что было между деплоем и падением:
$ journalctl --since "2026-05-13 09:30:00" --until "2026-05-13 09:45:00"
-p priority: severity filter
systemd использует syslog-уровни:
0 emerg — система не работает
1 alert — требуется немедленное действие
2 crit — критическая ошибка
3 err — ошибки
4 warning — предупреждения
5 notice — нормальные, но значимые
6 info — общая информация
7 debug — отладочный шум
# Только error и хуже (priority <= 3):
$ journalctl -u airflow-scheduler -p err
# Между err и emerg (включая):
$ journalctl -p err..emerg
# Только warning (одна priority):
$ journalctl -p warning..warning
Это сильно сокращает шум. На production «дай мне все error за сегодня»:
$ journalctl --since today -p err
—grep PATTERN: regex по сообщениям
$ journalctl -u airflow-scheduler --grep "ModuleNotFoundError"
$ journalctl --grep -i "timeout" # -i = case-insensitive
Regex компилируется в journalctl напрямую — быстрее, чем journalctl ... | grep ..., потому что фильтрация идёт на уровне журнала, без полного materializа.
-k: kernel messages
$ journalctl -k
$ journalctl -k --since today
Эквивалент dmesg. Hardware errors, OOM killer, network device up/down, кернел-warnings.
Если у тебя процесс был убит OOM-killer:
$ journalctl -k | grep -i 'killed process'
May 13 09:14:33 prod-vm kernel: Out of memory: Killed process 5432 (airflow), UID 1001, total-vm:8128MB
—output: формат вывода
# Default: short
$ journalctl -u nginx -n 1
May 13 08:42:11 prod-vm systemd[1]: Started nginx.service.
# Verbose: все fields структуры
$ journalctl -u nginx -n 1 -o verbose
Wed 2026-05-13 08:42:11.234567 UTC [s=abc123;i=4567;b=def...]
_BOOT_ID=...
_MACHINE_ID=...
_HOSTNAME=prod-vm
_SYSTEMD_UNIT=nginx.service
_PID=1
_UID=0
_GID=0
MESSAGE=Started nginx.service.
SYSLOG_IDENTIFIER=systemd
SYSLOG_FACILITY=3
PRIORITY=6
# JSON: для парсинга в jq / Python
$ journalctl -u nginx -n 1 -o json | jq
{
"_BOOT_ID": "...",
"_HOSTNAME": "prod-vm",
"_SYSTEMD_UNIT": "nginx.service",
"MESSAGE": "Started nginx.service.",
"PRIORITY": "6",
...
}
# JSON-pretty:
$ journalctl -u nginx -n 5 -o json-pretty
# Cat — только MESSAGE без metadata:
$ journalctl -u nginx -n 5 -o cat
Started nginx.service.
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
...
JSON-output — основа для интеграции journal с внешними мониторингами (Loki, Datadog).
Где живут журналы
$ ls /var/log/journal
4e3a1234567890abcdef.../
Каждая папка — machine-id. Внутри — бинарные .journal файлы. Размер обычно 50-200 МБ каждый, новые создаются по rotation policy.
Если /var/log/journal/ нет — журнал volatile (только в /run/log/journal/), теряется при reboot. Это типичная ситуация на минимальных Docker-образах. Чтобы сделать persistent:
$ sudo mkdir -p /var/log/journal
$ sudo systemd-tmpfiles --create --prefix /var/log/journal
$ sudo systemctl restart systemd-journald
Cleanup: —vacuum
Journal без retention может разрастись до десятков ГБ. Команды очистки:
# Сколько занимает:
$ journalctl --disk-usage
Archived and active journals take up 14.3G in the file system.
# Удалить старше 7 дней:
$ sudo journalctl --vacuum-time=7d
# Оставить не больше 2 ГБ:
$ sudo journalctl --vacuum-size=2G
# Оставить максимум 50 файлов:
$ sudo journalctl --vacuum-files=50
Постоянная политика — в /etc/systemd/journald.conf:
[Journal]
Storage=persistent
Compress=yes
SystemMaxUse=2G
SystemMaxFileSize=128M
MaxRetentionSec=2week
После правки:
$ sudo systemctl restart systemd-journald
Лимиты можно ставить независимо. journald применит самый строгий из активных.
/var/log/syslog vs journal
На Debian/Ubuntu обычно установлен rsyslog, и journald forwards в него. Так получается, что одни и те же сообщения попадают и в structured journal, и в текстовый /var/log/syslog.
$ ls /var/log/
auth.log daemon.log journal/ nginx/ syslog
Старые log-shippers (logrotate, fluent-bit для текстовых логов, custom grep-скрипты) работают с /var/log/syslog. Современные читают journald напрямую (через journalctl --output=json --follow или libsystemd API).
Для DE: на современных дистрибутивах читай journal, на legacy CentOS 6/7 или минимальных Docker-образах — /var/log/syslog или /var/log/messages (RHEL-семейство). Команда tail -f /var/log/syslog всё ещё работает на 99% систем.
DE-сценарий: «расследуй, почему упал DAG»
Реальная отладочная сессия:
# 1) Symptom: alert от Airflow — DAG failed at 06:42 UTC.
# 2) Что было с airflow-scheduler в этот момент:
$ journalctl -u airflow-scheduler --since "2026-05-13 06:30" --until "2026-05-13 06:50"
# 3) Видим Python traceback и database-error. Что было с postgresql?
$ journalctl -u postgresql --since "2026-05-13 06:30" --until "2026-05-13 06:50"
# 4) Postgres логи: 'connection refused'. Был ли он жив?
$ journalctl -u postgresql -p err --since today
# 5) Видим: OOM-killer убил postgres. Подтверждаем в kernel-journal:
$ journalctl -k --since "2026-05-13 06:40" --until "2026-05-13 06:45" | grep -i kill
# May 13 06:42:18 prod-vm kernel: Out of memory: Killed process 8888 (postgres)
# May 13 06:42:18 prod-vm kernel: memory.max=4G, memory.current=4G
# 6) Память исчерпана — потому что? Смотрим что съело RAM:
$ journalctl -k --since "2026-05-13 06:30" --until "2026-05-13 06:45" | grep -i 'memory'
# 7) Находим: spark-job писал в shared memory без лимитов.
# Решение: добавить MemoryMax= в spark.service unit-файл.
Без структурированного journal такой workflow занял бы час. С journalctl — 5 минут.
Когда journal не помогает
journald — для системных и сервисных логов. Не предназначен:
- Application logs с массой данных (Apache access.log, Spark app logs). Эти приложения пишут в свои файлы — journald был бы слишком тяжёлым.
- Audit logs с строгими требованиями (HIPAA, PCI). Для этого
auditdс/var/log/audit/. - Network packet capture (
tcpdump,wireshark). Это не текст.
Для каждого случая — свой инструмент. journal хорош для «что делает мой systemd-сервис прямо сейчас».
Полезный one-liner: топ failed-services с логами
# Какие сервисы упали и почему — одной командой:
$ systemctl --failed --no-legend | awk '{print $1}' | while read svc; do
echo "=== $svc ==="
journalctl -u "$svc" -n 5 -p err --no-pager
done
Полезно при разборе утреннего alert-а: «что упало за ночь?».
Попробуй сам
- Последние 50 строк journal:
journalctl -n 50 - Только error за сегодня:
journalctl -p err --since today - Logs ssh за час:
journalctl -u ssh --since "1 hour ago" - Сколько диска занимает journal:
journalctl --disk-usage - Live-tail systemd:
journalctl -f - Kernel за сегодня:
journalctl -k --since today
macOS-различия
- На macOS нет systemd -> нет journalctl. Логи через Apple Unified Logging:
log show --predicate 'subsystem == "com.apple.something"' --last 1h,log stream. - Для Docker Desktop логи контейнеров —
docker logs CONTAINERилиcolima logs(для colima). Внутри Linux VM — обычный journalctl, как на native Linux.
Главное
journalctl— единый журнал systemd. Structured (fields, metadata), фильтруемый.journalctl -u SVC— логи одного сервиса. Самая частая команда.-n N— последние N строк.-f— follow (live).-p err— только error+.--since "1 hour ago"/--until— временной фильтр. Форматы: today, yesterday, “X ago”, “YYYY-MM-DD HH:MM”.--grep PATTERN— regex по сообщениям.-k— kernel messages (эквивалент dmesg). Полезно для OOM, hardware errors.-o json— для парсинга в jq / Python. Основа интеграции с monitoring.- Persistent journal в
/var/log/journal/. Если нет — volatile (/run/log/journal), теряется при reboot. - Cleanup:
journalctl --vacuum-time=7dили--vacuum-size=2G. Постоянная политика в/etc/systemd/journald.conf. /var/log/syslog(или/var/log/messagesна RHEL) — параллельный текстовый log через rsyslog. Для legacy log-shippers.