Управление логгированием в systemd
Демон инициализации systemd де-факто уже стал стандартом в современных Linux-системах. На него перешли многие многие популярные дистрибутивы: Debian, RHEL/CentOS, Ubuntu (начиная с версии 15.04). В systemd используется принципиально иной (по сравнению с традиционным инструментом syslog) подход к логгированию. В его основе лежит централизация: cпециализированный компонент journal cобирает все системные сообщения (сообщения ядра, различных служб и приложений). Все логи сохраняются в бинарных файлах, что позволяет избежать сложностей с использованием парсеров для разных видов логов. При необходимости логи можно без проблем переконвертировать в другие форматы (более подробно об этом будет рассказано ниже). Journal может работать как совместно с syslog, так и полностью заменить его. Для просмотра логов используется утилита journalctl. Об особенностях и тонкостях работы с ней мы расскажем в этой статье.
Демон инициализации systemd де-факто уже стал стандартом в современных Linux-системах. На него перешли многие популярные дистрибутивы: Debian, RHEL/CentOS, Ubuntu (начиная с версии 15.04). В systemd используется принципиально иной (по сравнению с традиционным инструментом syslog) подход к логгированию.
В его основе лежит централизация: специализированный компонент journal cобирает все системные сообщения (сообщения ядра, различных служб и приложений). При этом специально настраивать отправку логов не нужно: приложения могут просто писать в stdout и stderr, a journal сохранит эти сообщения автоматически. Работа в таком режиме возможна и с Upstart, но он сохраняет все логи в отдельный файл, тогда как systemd сохраняет их в бинарной базе, что существенно упрощает систематизацию и поиск.
Хранение логов в бинарных файлах также позволяет избежать сложностей с использованием парсеров для разных видов логов. При необходимости логи можно без проблем переконвертировать в другие форматы (более подробно об этом будет рассказано ниже).
Journal может работать как совместно с syslog, так и полностью заменить его.
Для просмотра логов используется утилита journalctl. Об особенностях и тонкостях работы с ней мы расскажем в этой статье.
Установка времени
Одним из существенных недостатков syslog является сохранение записей без учёта часового пояса. В journal этот недостаток устранён: для логгируемых событий можно указывать как местное время, так и универсальное координированное время (UTC). Установка времени осуществляется с помощью утилиты timedatectl.
Просмотреть список часовых поясов можно при помощи команды:
$ timedatectl list-timezones
Установка нужного часового пояса осуществляется так:
$ timedatectl set-timezone <часовой пояс>
По завершении установки будет нелишним убедиться в том, что всё сделано правильно:
$ timedatectl status
Local time: Thu 2015-07-30 11:24:15 MSK
Universal time: Thu 2015-07-30 08:24:15 UTC
RTC time: Thu 2015-07-30 08:24:15
Time zone: Europe/Moscow (MSK, +0300)
NTP enabled: no
NTP synchronized: no
RTC in local TZ: no
DST active: n/a
В самой первой строке (Local time) должны быть показаны точное текущее время и дата.
Journalctl: просмотр логов
Для просмотра логов используется утилита journalctl.
Если ввести команду journalсtl без каких-либо аргументов, на консоль будет выведен огромный список:
-- Logs begin at Wed 2015-07-29 17:12:48 MSK, end at Thu 2015-07-30 11:24:15 MSK. --
Jul 29 17:12:48 host-10-13-37-10 systemd-journal[181]: Runtime journal is using 4.0M (max allowed 20.0M, trying to leave 30.0M free of 195.9M available → current limit 20.0M).
Jul 29 17:12:48 host-10-13-37-10 systemd-journal[181]: Runtime journal is using 4.0M (max allowed 20.0M, trying to leave 30.0M free of 195.9M available → current limit 20.0M).
Jul 29 17:12:48 host-10-13-37-10 kernel: Initializing cgroup subsys cpuset
Jul 29 17:12:48 host-10-13-37-10 kernel: Initializing cgroup subsys cpu
Jul 29 17:12:48 host-10-13-37-10 kernel: Initializing cgroup subsys cpuacct
Jul 29 17:12:48 host-10-13-37-10 kernel: Linux version 3.16.0-4-amd64 (debian-kernel@lists.debian.org) (gcc version 4.8.4 (Debian 4.8.4-1) ) #1 SMP Debian 3.16.7-ckt11-1+deb8u2 (2015-07-17)
Jul 29 17:12:48 host-10-13-37-10 kernel: Command line: BOOT_IMAGE=/boot/vmlinuz-3.16.0-4-amd64 root=UUID=b67ea972-1877-4c5b-a328-29fc0d6c7bc4 ro console=tty1 console=ttyS0 video=640x480 consoleblank=0 panic=15 c
Здесь мы привели лишь небольшой его фрагмент; на самом деле он включает гигантское количество записей.
Фильтрация логов
У утилиты journalctl есть опции, с помощью которых можно осуществлять фильтрацию логов и быстро извлекать из них нужную информацию.
Просмотр логов с момента текущей загрузки
С помощью опции -b можно просмотреть все логи, собранные с момента последней загрузки системы:
$ journalctl -b
Просмотр логов предыдущих сессий
С помощью journalctl можно просматривать информацию о предыдущих сессиях работы в системе — в некоторых случаях это бывает полезным.
Следует, однако, учитывать, что сохранение информации о предыдущих сессиях поддерживается по умолчанию не во всех дистрибутивах Linux. Иногда его требуется активировать
Для этого нужно открыть конфигурационный файл journald.conf, найти в нём раздел [Journal] и изменить значение параметра storage на persistent:
$ sudo nano /etc/systemd/journald.conf
...
[Journal]
Storage=persistent
Просмотреть список предыдущих загрузок можно с помощью команды:
$ journalctl --list-boots
0 9346310348bc4edea250555dc046b30c Thu 2015-07-30 12:39:49 MSK—Thu 2015-07-30 12:39:59 MSK
Её вывод состоит из четырёх колонок. В первой из них указывается порядковый номер загрузки, во второй — её ID, в третьей — дата и время. Чтобы просмотреть лог для конкретной загрузки, можно использовать идентификаторы как из первой, так и из второй колонки:
$ journalctl -b 0
или
$ journalctl -b 9346310348bc4edea250555dc046b30c
Фильтрация по дате и времени
В journalctl имеется также возможность просмотра логов за определённые периоды времени. Для этого используются опции —since и —until. Предположим, нам нужно просмотреть логи начиная с 17 часов 15 минут 20 июля 2015 года. Для этого потребуется будет выполнить команду:
$ journalctl --since "2015-07-20 17:15:00"
Если с опцией since не будет указано никакой даты, на консоль будут выведены логи начиная с текущей даты. Если дата указана, но при этом не указано время, будет применено значений времени по умолчанию — «00:00:00». Секунды также указывать не обязательно (в этом случае применяется значение по умолчанию — 00).
Можно воспользоваться и вот такими человекопонятными конструкциями:
$ journalctl ---since yesterday
$ journalctl --since 09:00 --until now
$ journalctl --since 10:00 --until "1 hour ago"
Фильтрация по приложениям и службам
Для просмотра логов конкретного приложения или службы используется опция -u, например:
$ journalctl -u nginx.service
Приведённая команда выведет на консоль логи веб-сервера nginx.
Нередко возникает необходимость просмотреть логи какой-либо службы за определённый период времени. Это можно сделать при помощи команды вида:
$ journalctl -u nginx.service --since yesterday
C опцией -u также используется фильтрация по дате и времени, например:
$ journalctl -u nginx.service -u php-fpm.service —since today
Благодаря этому можно отслеживать взаимодействие различных служб и получать информацию, которую нельзя было бы получить при отслеживании соответствующих процессов по отдельности.
Фильтрация по процессам, пользователям и группам
Просмотреть логи для какого-либо процесса можно, указав в команде journalctl его идентификационный номер (PID), например:
$ journalctl _PID=381
Для просмотра логов процессов, запущенных от имени определённого пользователя или группы, используются фильтры _UID и _GID соответственно. Предположим, у нас имеется веб-сервер, запущенный от имени пользователя www-data. Определим сначала ID этого пользователя:
$id -u www-data
33
Теперь можно просмотреть логи всех процессов, запущенных от имени этого пользователя:
$ journalctl _UID=33
Вывести на консоль список пользователей, о которых имеются записи в логах, можно так:
$ journalctl -F _UID
Для просмотра аналогичного списка пользовательских групп используется команда:
$ journalctl -F _GUID
С командной journalctl можно использовать и другие фильтры. Просмотреть список всех доступных фильтров можно, выполнив команду
$ man systemd.journal-fields
Фильтрация по пути
Просмотреть логи для какого-либо процесса также можно, указав путь к нему, например:
$ journalctl /usr/bin/docker
Иногда таким способом можно получить более подробную информацию (например, просмотреть записи для всех дочерних процессов).
Просмотр сообщений ядра
Для просмотра сообщений ядра используется опция -k или −−dmesg:
$ journalctl -k
Приведённая команда покажет все сообщения ядра для текущей загрузки. Чтобы просмотреть сообщения ядра для предыдущих сессий, нужно воспользоваться опцией -b и указать один из идентификаторов сессии (порядковый номер в списке или ID):
$ journalctl -k -b -2
Фильтрация сообщений по уровню ошибки
Во время диагностики и исправления неполадок в системе нередко требуется просмотреть логи и выяснить, есть ли в них сообщения о критических ошибках. Специально для этого в journalctl предусмотрена возможность фильтрации по уровню ошибки. Просмотреть сообщения обо всех ошибках, имевших место в системе, можно с помощью опции -p:
$ journalctl -p err -b
Приведённая команда покажет все сообщения об ошибках, имевших место в системе.
Эти сообщения можно фильтровать по уровню. В journal используется такая же классификация уровней ошибок, как и в syslog:
- 0 — EMERG (система неработоспособна);
- 1 — ALERT (требуется немедленное вмешательство);
- 2 — CRIT (критическое состояние);
- 3 — ERR (ошибка);
- 4 — WARNING (предупреждение);
- 5 — NOTICE (всё нормально, но следует обратить внимание);
- 6 — INFO (информационное сообщение);
- 7 —DEBUG (отложенная печать).
Коды уровней ошибок указываются после опции -p.
Запись логов в стандартный вывод
По умолчанию journalctl использует для вывода сообщений логов внешнюю утилиту less. В этом случае к ним невозможно применять стандартные утилиты для обработки текстовых данных (например, grep). Эта проблема легко решается: достаточно воспользоваться опцией −−no-pager, и все сообщения будут записываться в стандартный вывод:
$ journalctl --no-pager
После этого их можно будет передать другим утилитам для дальнейшей обработки или сохранить в текстовом файле.
Выбор формата вывода
C помощью опции -o можно преобразовывать данные логов в различные форматы, что облегчает их парсинг и дальнейшую обработку, например:
$ journalctl -u nginx.service -o json
{ "__CURSOR" : "s=13a21661cf4948289c63075db6c25c00;i=116f1;b=81b58db8fd9046ab9f847ddb82a2fa2d;m=19f0daa;t=50e33c33587ae;x=e307daadb4858635", "__REALTIME_TIMESTAMP" : "1422990364739502", "__MONOTONIC_TIMESTAMP" : "27200938", "_BOOT_ID" : "81b58db8fd9046ab9f847ddb82a2fa2d", "PRIORITY" : "6", "_UID" : "0", "_GID" : "0", "_CAP_EFFECTIVE" : "3fffffffff", "_MACHINE_ID" : "752737531a9d1a9c1e3cb52a4ab967ee", "_HOSTNAME" : "desktop", "SYSLOG_FACILITY" : "3", "CODE_FILE" : "src/core/unit.c", "CODE_LINE" : "1402", "CODE_FUNCTION" : "unit_status_log_starting_stopping_reloading", "SYSLOG_IDENTIFIER" : "systemd", "MESSAGE_ID" : "7d4958e842da4a758f6c1cdc7b36dcc5", "_TRANSPORT" : "journal", "_PID" : "1", "_COMM" : "systemd", "_EXE" : "/usr/lib/systemd/systemd", "_CMDLINE" : "/usr/lib/systemd/systemd", "_SYSTEMD_CGROUP" : "/", "UNIT" : "nginx.service", "MESSAGE" : "Starting A high performance web server and a reverse proxy server...", "_SOURCE_REALTIME_TIMESTAMP" : "1422990364737973" }
Объект json можно представить в более структурированном и человекочитаемом виде, указав формат json-pretty или json-sse:
$ journalctl -u nginx.service -o json-pretty
{
"__CURSOR" : "s=13a21661cf4948289c63075db6c25c00;i=116f1;b=81b58db8fd9046ab9f847ddb82a2fa2d;m=19f0daa;t=50e33c33587ae;x=e307daadb4858635",
"__REALTIME_TIMESTAMP" : "1422990364739502",
"__MONOTONIC_TIMESTAMP" : "27200938",
"_BOOT_ID" : "81b58db8fd9046ab9f847ddb82a2fa2d",
"PRIORITY" : "6",
"_UID" : "0",
"_GID" : "0",
"_CAP_EFFECTIVE" : "3fffffffff",
"_MACHINE_ID" : "752737531a9d1a9c1e3cb52a4ab967ee",
"_HOSTNAME" : "desktop",
"SYSLOG_FACILITY" : "3",
"CODE_FILE" : "src/core/unit.c",
"CODE_LINE" : "1402",
"CODE_FUNCTION" : "unit_status_log_starting_stopping_reloading",
"SYSLOG_IDENTIFIER" : "systemd",
"MESSAGE_ID" : "7d4958e842da4a758f6c1cdc7b36dcc5",
"_TRANSPORT" : "journal",
"_PID" : "1",
"_COMM" : "systemd",
"_EXE" : "/usr/lib/systemd/systemd",
"_CMDLINE" : "/usr/lib/systemd/systemd",
"_SYSTEMD_CGROUP" : "/",
"UNIT" : "nginx.service",
"MESSAGE" : "Starting A high performance web server and a reverse proxy server...",
"_SOURCE_REALTIME_TIMESTAMP" : "1422990364737973"
}
Помимо JSON данные логов могут быть преобразованы в следующие форматы:
- cat — только сообщения из логов без служебных полей;
- export — бинарный формат, подходит для экспорта или резервного копирования логов;
- short — формат вывода syslog;
- short-iso — формат вывода syslog с метками времени в формате ISO 8601;
- short-monotonic — формат вывода syslog c метками монотонного времени (monotonic timestamp);
- short-precise — формат вывода syslog с метками точного времени (время событий указывается с точностью до микросекунд);
- verbose — максимально подробный формат представления данных (включает даже те поля, которые в других форматах не отображаются).
Просмотр информации о недавних событиях
Опция -n используется для просмотра информации о недавних событиях в системе:
$ journalctl -n
По умолчанию на консоль выводится информация о последних 10 событиях. С опцией -n можно указать необходимое число событий:
$ journalctl -n 20
Просмотр логов в режиме реального времени
Сообщения из логов можно просматривать не только в виде сохранённых файлов, но и в режиме реального времени. Для этого используется опция -f:
$ journalctl -f
Управление логгированием
Определение текущего объёма логов
Со временем объём логов растёт, и они занимают всё больше места на жёстком диске. Узнать объём имеющихся на текущий момент логов можно с помощью команды:
$ journalctl --disk-usage
Journals take up 16.0M on disk.
Ротация логов
Настройка ротации логов осуществляется с помощью опций −−vacuum-size и −−vacuum-time.
Первая из них устанавливает предельно допустимый размер для хранимых на диске логов (в нашем примере — 1 ГБ):
$ sudo journalctl --vacuum-size=1G
Как только объём логов превысит указанную цифру, лишние файлы будут автоматические удалены.
Аналогичным образом работает опция −−vacuum-time. Она устанавливает для логов срок хранения, по истечении которого они будут автоматически удалены:
$ sudo journalctl --vacuum-time=1years
Настройка ротации логов в конфигурационном файле
Настройки ротации логов можно также прописать в конфигурационном файле /еtc/systemd/journald.conf, который включает в числе прочих следующие параметры:
- SystemMaxUse= максимальный объём, который логи могут занимать на диске;
- SystemKeepFree= объём свободного места, которое должно оставаться на диске после сохранения логов;
- SystemMaxFileSize= объём файла лога, по достижении которого он должен быть удален с диска;
- RuntimeMaxUse= максимальный объём, который логи могут занимать в файловой системе /run;
- RuntimeKeepFree= объём свободного места, которое должно оставаться в файловой системе /run после сохранения логов;
- RuntimeMaxFileSize= объём файла лога, по достижении которого он должен быть удален из файловой системы /run.
Централизованное хранение логов
Одной из самых распространённых задач в работе системного администратора является настройка сбора логов с нескольких машин с последующим помещением в централизованное хранилище.
В systemd предусмотрены специальные компоненты для решения этой задачи: systemd-journal-remote, systemd-journal-upload и systemd-journal-gatewayd.
С помощью команды systemd-journal-remote можно принимать логи с удалённых хостов и сохранять их (на каждом их этих хостов должен быть запущен демон systemd-journal-gatewayd), например:
$ systemd-journal-remote −−url https://some.host:19531/
В результате выполнения приведённой команды логи с хоста https://some.host/ будут сохранены в директории var/log/journal/some.host/remote-some~host.journal .
С помощью команды systemd-journal-remote можно также складывать имеющиеся на локальной машине логи в отдельную директорию, например:
$ journalctl -o export | systemd-journal-remote -o /tmp/dir -
Команда systemd-journal-upload используется для загрузки логов с локальной машины в удалённое хранилище:
$ systemd-journal-upload --url https://some.host:19531/
Как видно из приведённых примеров, «родные» утилиты systemd для поддержки централизованного логгирования просты и удобны в работе. Но они, к сожалению, пока что включены далеко не во все дистрибутивы, а только в Fedora и ArchLinux.
Пользователи других дистрибутивов пока что приходится передавать логи в syslog или rsyslog, которые затем пересылают их по сети. Ещё одно решение проблемы централизованного логгирования было предложено разработчиками утилиты journal2gelf, включённой в официальный репозиторий systemd: вывод journalсtl в формате JSON конвертируется в формат GELF, а затем передаётся приложению для сбора и анализа логов Graylog. Решение не очень удобное, но ничего лучше в текущей ситуации придумать нельзя. Остаётся только ждать, когда «родные» компоненты будут добавлены во все дистрибутивы.
Заключение
Как видно из проделанного рассмотрения, systemd journal представляет собой гибкий и удобный инструмент для сбора системных данных и данных приложений. Высокого уровня гибкости и удобства удалось добиться, во-первых, благодаря централизованному подходу к логгированию, а во-вторых — благодаря автоматической записи в логи подробных метаданных. С помощью journalctl можно просматривать логи, получая необходимую информацию для анализа работы и отладки различных системных компонентов.
Если у вас есть вопросы и дополнения — добро пожаловать в комментарии. Разговор о systemd и его компонентах будет продолжен в следующих публикациях.