Текстовый вывод журнала удобен человеку: строчки читаются глазами, ошибки выделяются, время видно сразу. Машинам нужен другой порядок: именованные поля, однозначные типы, предсказуемая структура. Журнальная утилита умеет отдавать записи в формате JSON, и это превращает системный журнал из текста для чтения в источник для программ: выгрузки парсятся без хрупких регулярных выражений, агрегации строятся за минуты, системы анализа получают готовые поля. Статья разбирает устройство формата, команды выгрузки, конвейеры с jq, конвертацию времени и подключение JSON-потока к внешним системам.
Формат JSON в журнале и его поля от сообщения до метки времени
Каждая запись журнала в JSON-режиме становится объектом с набором полей. Текст сообщения живёт в поле MESSAGE, приоритет в PRIORITY, источник в поле юнита, рядом идентификатор процесса, имя хоста и метка времени. Поля с двойным подчёркиванием в начале добавлены самим сборщиком и считаются доверенными: их нельзя подделать из приложения, поэтому на них опираются фильтры и отчёты.
Главная ловушка формата ожидаема и известна заранее: значения приходят строками. Приоритет ошибки выглядит как строка с цифрой, а не число, и сравнение в парсере обязано учитывать это. Опытные скрипты приводят типы явно, а не полагаются на автоматическое преобразование, и эта привычка экономит часы недоумений.
Состав полей зависит от источника. Системные юниты дают полный набор служебных полей, приложения, встроившиеся в журнал, добавляют собственные: идентификаторы запросов, коды результатов, идентификаторы пользователей. Именно собственные поля делают формат ценным: они попадают в выгрузки автоматически и доступны тем же фильтрам, что и стандартные.
Отдельная строка про время. Метка хранится в микросекундах от начала эпохи, длинным числом. Для человека она нечитаема, для машины удобна: точный и однозначный формат без зон и двусмысленностей. Раздел про конвертацию ниже закрывает вопрос читаемых отчётов.
Полнота полей делает выгрузку самодостаточной: в объекте достаточно сведений, чтобы восстановить контекст записи без обращения к серверу-источнику. Это важно для протоколов, файл выгрузки, открытый через полгода на другой машине, обязан объяснять себя сам.
Вывод журнала в JSON и варианты потоковой передачи
Базовый ключ формата переводит вывод в построчный JSON: одна запись, одна строка. Такой поток удобен конвейерам и файлам, каждая строка парсится независимо. Вариант с отступами печатает объекты в развёрнутом виде для чтения человеком, а потоковый вариант оборачивает записи в формат событий для живых потребителей:
journalctl -u nginx -n 5 -o json-pretty --no-pager
journalctl --since "-1h" -p err -o json > errors.json
Формат комбинируется со всеми фильтрами журнала: юниты, приоритеты, окна времени и загрузки. Правильный порядок работы фильтрация средствами журнала, формат на выходе. Журнал сам умеет выбирать записи быстро и по индексам, и перекладывать эту работу на внешний парсер расточительно.
Сокращение состава полей превращает выгрузку в компактный транспорт. Ключ вывода с перечнем полей оставляет в объектах только необходимое: сообщение и приоритет для сводки, сообщение и время для протокола. Лишние килобайты служебных полей не едят канал и не мельтешат перед глазами.
Для живых потребителей предусмотрен потоковый режим с обёрткой событий: записи оборачиваются в формат, который понимают браузерные и серверные клиенты подписки. Редкий, но честный сценарий: собственный наблюдатель за журналом без polling-циклов и самодельного слежения за файлами.
Выбор формата это выбор потребителя: построчный вариант для конвейеров и файлов, развёрнутый для чтения человеком при разовом разборе, потоковый для постоянных подписчиков. Путаница заметна сразу: конвейер, читающий развёрнутый вариант, ломается на первой многострочной записи, а человек, читающий построчный поток, устаёт на второй сотне строк. У каждого сценария свой формат, и это правило читается в первых строках любого конвейера.
Парсинг через jq от простых выборок до агрегаций
Инструмент jq читает поток JSON-объектов и применяет фильтры. Первая полезная команда оставляет только текст сообщений, и вывод становится похож на классический журнал, но собранный по вашим правилам. Вторая ступень добавляет условия: отбор по значению любого поля, включая те, по которым журнальная утилита не фильтрует напрямую:
journalctl --since "-1h" -o json | jq -r 'select(.PRIORITY=="3" or .PRIORITY=="4") | .MESSAGE' | sort | uniq -c | sort -rn | head
Конвейер из предыдущего блока это готовая сводка проблем за час: журнал отфильтровал по времени, парсер отобрал ошибки и предупреждения по строковому полю приоритета, счётчик повторов отсортировал шум по частоте. Каждый участник конвейера делает свою часть, и ни один не пытается парсить текст регулярным выражением.
Третья ступень агрегации собирает записи в массив и считает по группам. Ключ slurp накапливает вход, группировка по полю приоритета с подсчётом элементов даёт распределение ошибок по уровням за окно. Так строятся ежедневные сводки: одно число вчерашних ошибок по каждому уровню вместо ручного просмотра.
Готовая форма суточной сводки выглядит так: