Источник: A journalctl mini-tutorial
Автор: Alexander Schmolck · 20 февраля 2020 · 9 мин чтения
journalctl — это инструмент для просмотра логов ядра и служб пользовательского пространства, доступный во всех основных дистрибутивах Linux. Основных — в том смысле, что с systemd: это интерфейс к journald, единой системе журналирования systemd. Так что хотя он и может не покорить вас уникально элегантным и ортогональным дизайном или реализацией, вы можете обнаружить, что journalctl (иногда) даёт желанные преимущества перед привычным tail или less по /var/log/.... Например, вот как найти все строки лога с уровнем warning и выше для службы nginx (systemd):
journalctl -p warning -u nginx --since today- Удобства: ANSI-подсветка на основе серьёзности (severity), возможность «tail -f» сразу нескольких служб.
- Лёгкая фильтрация по серьёзности, диапазонам времени (
--since/--until), именам служб (-u/--unit), исполняемым файлам (_EXE=…) или PID (_PID=...). И, возможно, более спорное: tail-инг (-f/--follow), tac-инг (-r/--reverse) и grep-инг (--grep PATTERN), хотя последнее поддерживается только в более свежих версиях (и требует флага сборки, который в некоторых дистрибутивах отключён). - Более полные (мета)данные: journald внутренне использует двоичный формат хранения логов, который богаче того, что вы найдёте в /var/log/syslog и т.п. (временные метки всегда микросекундного уровня, и вы получаете PID, теги контейнеров, командную строку запуска, а также неповреждённые многострочные записи лога и т.д.). Также уникальные, однозначные идентификаторы для множества вещей: например, загрузок (boots) или идентификаторов сообщений (message-ids).
- Один единый инструмент для всех логов ядра и пользовательского пространства (не нужно копаться в log-rotate архивах с zgrep).
- Хорошее автодополнение для служб (
-u/--unit), полей (-F/--field) и т.д. в bash и zsh. Часто проще, чем искать нужный файл лога. - Поддержка разных форматов вывода (например, json для дальнейшей обработки, стандартный syslog-стиль для людей).
- Для по-настоящему преданных он предлагает API запросов на Python.
Одно несомненное убийственное преимущество (когда оно вам нужно) — более полные метаданные. Вы можете получить PID или полные командные строки запуска даже после того, как служба была остановлена. Например, вы можете использовать journalctl --since '1 week ago' _COMM=sudo, чтобы найти время, командную строку и pwd всех вызовов sudo за последнюю неделю — я понятия не имею, как иначе получить эту информацию (/var/log/auth.log не содержит её на моей машине). Поскольку он может выводить json, вы также можете резать и нарезать информацию инструментами вроде jq.
В редких случаях также может быть очень полезен доступ к полным, неповреждённым данным лога: обычный текстовый формат, который вы найдёте в /var/log/…, заменяет некоторые непечатаемые символы (как переводы строк в многострочных записях лога) восьмеричными escape-последовательностями (#012 для переводов строк). И он «полезно» делает это необратимым образом (потому что #012 также выводится как #012; попробуйте logger $'foo\n#012bar' && tail -1 /var/log/syslog). journalctl -o json покажет вам настоящую многострочную строку.
Я предпочитаю использовать нормальный сортируемый формат даты с субсекундным разрешением и без имени хоста. Так что давайте представим, что все примеры выполняются после создания алиаса:
alias journalctl='journalctl --utc -o short-precise-iso --no-hostname'Начнём с просмотра всех сообщений лога, с самого начала записанной истории, в пейджере:
journalctlЕсли это даёт вам: unknown output format 'short-precise-iso' или unrecognized option: '--no-hostname' — у вас более старая версия. Если же вы читаете no journal files were found, то у вас, вероятно, либо не хватает нужных привилегий, либо система была плохо настроена. В любом случае переходите вперёд к разделу Подводные камни и раздражители journalctl для некоторых обходных путей.
Просмотр живых (-f) сообщений лога уровня ядра (-k) (попробуйте отключить и снова подключить клавиатуру или мышь, пока это выполняется!).
journalctl -k -fСообщения уровня ядра (-k) с последней загрузки (-b1) — возможно, вы захотите дополнительно попробовать dmesg для получения дополнительных данных:
journalctl -k -b1-k эквивалентен указанию _TRANSPORT=kernel (другие варианты: «driver», «syslog», «journal», «stdout» и «audit» — для таких вещей, как APPARMOR).
Вот 4 эквивалентных способа показать логи для ssh на моей машине:
journalctl -n20 -u ssh.service # для юнитов systemd
journalctl -n20 -u ssh # идентично
journalctl -n20 _SYSTEMD_UNIT=ssh # (почти) то же, но...
journalctl -n20 -u 'ss*' # -u также допускает подстановочные знаки
# и добавляет больше всего [*]journalctl -n20 SYSLOG_IDENTIFIER=sshd # обратите внимание, это не то же самоеjournalctl -n20 /usr/sbin/sshd # путь к исполняемому файлу
journalctl -n20 _EXE=/usr/sbin/sshd # то же
journalctl -n20 _COMM=sshd # похожеПервый набор работает только если служба управляется systemd, второй полностью общий. Возможность указать службу по имени исполняемого файла особенно удобна, когда вы разворачиваете через симлинк на каталог с хешем версии в системе контроля версий и хотите быстро проверить, не залогировала ли предыдущая сборка конкретную ошибку.
Если ваша служба — это docker-контейнер, запущенный с journald в качестве бэкенда логирования, то вы должны быть способны сделать что-то вроде: journalctl CONTAINER_NAME=webserver.
[*] В дополнение к разрешению подстановочных знаков -u также добавляет сообщения о юните, а не от юнита — например, когда он падает с coredump; это эквивалентно добавлению ещё нескольких селекторов, хотя документация не разъясняет, каких именно.
Вы можете использовать относительное или абсолютное время начала и окончания (но без T между датой и временем!).
journalctl --since '1h ago' --until '10 min ago'
journalctl --since -1h --until -10min # то же
journalctl --since '2020-02-01 03:00' # NB: ' ', а не T
journalctl --since '2020-02-01T03:00' # ОШИБКА :(journalctl -f -p warning # покажи мне предупреждения и выше по мере их появления
journalctl -fp 4 # то же
journalctl -p err # покажи все ошибки (и выше)
journalctl -p 3 # то же
journalctl -p err -n1 _COMM=sudo # последняя ошибка аутентификации sudo
journalctl -p info..warning # сообщения с info до warningjournalctl -p debug..debug # *только* отладочные сообщения
journalctl -p 7..4 | grep error # что логируется с неправильным приоритетом?Есть 8 (!) уровней серьёзности с убывающим приоритетом от 0 до 7, но я обычно использую только warning(4) и err(3).
Помимо исправления формата временных меток, самая полезная опция — логирование в формате json для получения дополнительных метаданных по сравнению с выводом по умолчанию (наиболее полезны _PID, _UID, _GID и CMDLINE), которые затем можно обработать с помощью jq.
journalctl -o json-pretty # полные метаданные в json
journalctl -o json | jq . # похоже, но с подсветкой
journalctl -o cat # то же, что --no-pager
journalctl --utc -o short-iso-precise # нормальные временные метки
journalctl -o verbose --all # включая непечатаемые символы
journalctl -o verbose --all --output-fields=MESSAGEПоказать таблицу того, кто запускал sudo за последнюю неделю, с какой командной строкой, каким PWD и каким пользователем?
journalctl --since '1 week ago' _COMM=sudo -o json \
| jq -r '(.__REALTIME_TIMESTAMP|tonumber|(./1e6)|todate) + "\t" + ._CMDLINE + "\t" + .MESSAGE' \
| column -ts $'\t'# без -o cat journalctl выведет дополнительную строку
# с доступным диапазоном времени логов
journalctl -o cat -p err -u ssh --since today | wc -lКакие исполняемые файлы логировали ошибки с уровнем логирования ниже error за последний месяц?
journalctl --since -1month -p 7..4 -o json | jq -r 'select (.MESSAGE | contains("error")) | ._EXE' | sort -uЭто весьма полезно, если вы разворачиваете новые версии вашей службы в разные каталоги с хешем версии и создаёте симлинк, например, на /opt/fooservice/current (для лёгкого отката):
journalctl -p err /opt/fooservice/9eac776/bin/fooservicejournalctl -f -e -p err docker --since today # -e подразумевает -n1000# Только те два pid; одинаковые ключи селектора объединяются по ИЛИ (OR),
# разные — по И (AND) (если только вы не добавите `+` между ними);
# здесь мы также случайно используем _SYSTEMD_UNIT вместо -u
journalctl _SYSTEMD_UNIT=docker --since '2018-11-01 14:00' --until '2018-11-13 14:00' _PID=123 _PID=456journalctl [FLAGS] [KEY=VALUE…]
Селекторы KEY=VALUE объединяются по ИЛИ (OR) для повторяющихся KEY и по И (AND) для различных KEY (если вы не добавите + между ними). У самых полезных KEY есть также эквиваленты в виде флагов или неявные эквиваленты. Например, -u docker эквивалентен _SYSTEMD_UNIT=docker, а journalctl _EXE=/usr/bin/dbus-daemon — то же, что просто journalctl /usr/bin/dbus-daemon.
Теги, которые особенно полезны (особенно поскольку по ним можно делать запросы и после выхода процесса): _EXE, _CMDLINE, _UID, _PID и _CONTAINER_TAG.
Возможность надёжно и легко задать вместе развёрнутую версию кода, уровень ошибки и диапазон времени может быть приятной.
Первое дело — нажмите <TAB>: в вашем шелле автодополнение должно быть доступно из коробки.
journalctl <TAB> для дополнения фильтров, journalctl -o<TAB> для форматов вывода и т.д.
За какой диапазон времени у меня есть логи?
journalctl | head -1О каких службах systemd у меня есть логи?
journalctl -F _SYSTEMD_UNIT # также попробуйте _COMM _EXE и _CMDLINEОт каких пользователей работают службы, которые что-то залогировали (замените _UID/-u на _GID/-g для групп)?
journalctl -F _UID | xargs -n1 id -nuЧтобы вместо этого увидеть имена исполняемых файлов, отправивших сообщения лога, выполните journalctl -F _EXE, а для вызовов командной строки — journalctl -F _CMDLINE. Аналогично, чтобы увидеть все коды фатальных errno: journalctl -F ERRNO |xargs -n1 errno.
Какие есть поля-селекторы? Показать до 5 значений каждого
for f in $(journalctl --fields); do
echo ===========$f;
journalctl -F $f;
done | grep -A5 ========Сколько места занимают логи журнала на диске?
journalctl --disk-usageВы можете сделать некоторую экстренную очистку с помощью sudo journalctl --vacuum-size=1G или sudo journalctl --vacuum-time=3days, но вообще это настраивается в journald.conf(5).
POSIX-способ — это утилита logger, которая в Linux также принимает флаг --journald, позволяющий заполнить все поля метаданных.
logger -p err 'something bad happened'Но у systemd также есть собственный systemd-cat:
echo 'something informational happened' | systemd-cat -p info
# почти эквивалентно, но также захватывает stderr
systemd-cat -p info echo 'something informational happened'Напоследок несколько вещей, о которых стоит знать:
- Если вы получаете
no journal files were found, у вас, вероятно, либо не хватает нужных привилегий, либо система была плохо настроена; если вы можете выполнитьsudo journalctl, попросите кого-нибудь (возможно, себя) выполнитьsudo usermod -a -G systemd-journal $YOUR_USER_NAME. - Временные метки по умолчанию не в формате ISO, а поддержка высокого разрешения временных меток в формате ISO есть только в достаточно свежих версиях (
-o short-iso-precise), так что если вы видитеunknown output format 'short-precise-iso', вы можете либо обновиться (удачи!), либо переключиться на-o short-preciseили-o short-iso. Кроме того, некоторые команды вроде--list-boots«полезно» просто игнорируют флаг-oцеликом. - Разделителем времени/даты должен быть пробел; писать «T» между датой и временем в
--sinceили--untilнельзя (вопреки стандарту ISO и обычной практике CLI, где обычно допустима либо только версия с T, либо обе; это также necessitирует постоянное заключение временных меток в кавычки). - Уровней логирования (
-p) слишком много, и названы они непоследовательно:warning(неwarn), ноerr(неerror) и т.д.: “emerg” (0), “alert” (1), “crit” (2), “err” (3), “warning” (4), “notice” (5), “info” (6), “debug”. Впрочем, это просто наследие syslog. - Невозможно запросить доступные поля для селектора. Разве не было бы приятно, если бы
journalctl -F _PID _SYSTEMD_UNIT=docker.serviceпросто выводил pid для docker.service? - Как и systemd в целом, journalctl отвергает unix-мантру «делай одну вещь хорошо, с минимальной реализацией» в пользу предложения mishmash-функциональности (включая tail -f, grep, sed, awk, tac и т.д.). Часть этого можно оправдать как способ избежать проблем с буферизацией и производительностью.
- Качество реализации исторически было неравномерным. Я видел, как старые версии journalctl часто падали при выходе, оставляя терминал в испорченном состоянии (выполните
reset, если это случится с вами). Другой пример: если вы ищете в пустом диапазоне (например,-p err --since … --until…и нет ошибок), journalctl может выдать вам фиктивное сообщение об ошибке (No journal files were found. Failed to determine timestamp: Cannot assign requested address). К счастьцу, эти проблемы, похоже, исчезли с Ubuntu 18.04 и новее.