Запросы к journald с journalctl в скриптах
Фильтруйте записи журнала systemd по модулю, приоритету и времени для автоматического анализа инцидентов
«Запросы к journald с journalctl в скриптах» — бесплатный урок DevOps Bootcamp на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения DevOps Bootcamp, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс DevOps Bootcamp содержит 4 уроков всего.
Почему journald важен для первичного анализа инцидентов
Современные системы Linux под управлением systemd централизуют весь вывод журналов в журнале — структурированном двоичном хранилище, которым управляет systemd-journald. В отличие от обычных текстовых файлов в /var/log, каждая запись журнала содержит расширенные метаданные: имя единицы, уровень приоритета, PID, UID, временную метку и другие сведения.
В автоматизированных сценариях первичного анализа инцидентов эти метаданные позволяют Вам:
- Фильтровать журналы для одной службы без цепочек
grep - Ограничивать запросы точными временными интервалами (последние 15 минут, начиная с развёртывания)
- Выводить только критические сообщения и сообщения об ошибках, игнорируя лишние данные
- Напрямую передавать структурированный вывод в конвейеры оповещений
Доступ ко всем этим возможностям предоставляет journalctl. В этом уроке Вы научитесь программно управлять им внутри сценариев Bash.
Основной вызов journalctl
Простейшая форма journalctl выводит весь журнал. В сценариях это почти никогда не требуется — всегда добавляйте хотя бы один фильтр. Ниже приведены наиболее распространённые параметры, которые Вы будете объединять:
-u <unit>— фильтр по единице systemd (например,nginx.service)-p <priority>— фильтр по приоритету syslog (0=emerg … 7=debug)--since/--until— временной интервал-n <N>— последние N строк--no-pager— отключить интерактивную постраничную навигацию (необходимо в сценариях)-o <format>— формат вывода (short,json,catи т. д.)
Всегда передавайте --no-pager в неинтерактивных сценариях, чтобы journalctl не пытался запустить less и не зависал.
#!/usr/bin/env bash
# Print the last 20 lines of the nginx service journal
journalctl --no-pager -u nginx.service -n 20Фильтрация по единице Systemd
Параметр -u принимает любое допустимое имя единицы. Его можно указывать несколько раз для объединения единиц, что полезно, когда одно приложение состоит из нескольких служб (например, API и связанной с ним базы данных).
Имена единиц соответствуют шаблону <name>.service, <name>.socket, <name>.timer и т. д. Поддерживается подстановка шаблонов: -u 'myapp*' соответствует myapp-api.service, myapp-worker.service и другим именам.
В сценарии первичного анализа Вы обычно получаете имя единицы в качестве аргумента, поэтому фильтр получается динамическим.
#!/usr/bin/env bash
# Usage: ./unit_logs.sh nginx.service
UNIT="${1:?Usage: $0 <unit>}"
echo "=== Journal for ${UNIT} (last 50 lines) ==="
journalctl --no-pager -u "${UNIT}" -n 50
# Combine two related units
echo "=== Combined: api + worker ==="
journalctl --no-pager -u myapp-api.service -u myapp-worker.service -n 30Уровни приоритета и флаг -p
Флаг -p соответствует стандартным уровням приоритета syslog:
0— emerg1— alert2— crit3— err4— warning5— notice6— info7— debug
Можно указать один уровень (-p err), чтобы просматривать только сообщения этого уровня, или диапазон (-p emerg..err), чтобы получать всё — от экстренных сообщений до ошибок. Это наиболее распространённый выбор для автоматического оповещения.
Именованные псевдонимы (err, warning, crit) принимаются наряду с числовыми значениями.
#!/usr/bin/env bash
# Extract only error-level and above entries for sshd
journalctl --no-pager \
-u sshd.service \
-p emerg..err \
--since "1 hour ago"
# Exit non-zero if any errors were found (useful in CI health checks)
ERROR_COUNT=$(journalctl --no-pager -u sshd.service -p emerg..err \
--since "1 hour ago" --output=cat | wc -l)
if [[ "${ERROR_COUNT}" -gt 0 ]]; then
echo "[ALERT] ${ERROR_COUNT} error(s) detected in sshd" >&2
exit 1
fiФильтрация по временному интервалу с помощью --since и --until
Фильтры по времени — основа запросов за период инцидента. journalctl принимает гибкие метки времени в удобном для человека формате:
- Относительные:
"10 minutes ago","2 hours ago","yesterday" - Абсолютные:
"2026-06-11 14:00:00" - Специальные ключевые слова:
today,yesterday,-1h(сокращённая форма)
В сценариях развёртывания обычно сохраняют метку времени непосредственно перед развёртыванием, а затем запрашивают журнал начиная с этого момента, чтобы обнаружить регрессии, внесённые выпуском.
#!/usr/bin/env bash
# Record deploy start time, then check logs afterwards
DEPLOY_START=$(date +"%Y-%m-%d %H:%M:%S")
echo "Deploying at ${DEPLOY_START}..."
# ... your deploy steps here ...
sleep 2 # simulate deploy
echo "=== Journal since deploy start ==="
journalctl --no-pager \
-u myapp.service \
--since "${DEPLOY_START}" \
-p emerg..warningСтруктурированный вывод в формате JSON
Для конвейеров, обрабатывающих данные автоматически, передайте -o json (по одному объекту JSON в строке, NDJSON) или -o json-pretty (форматированный вывод). Каждый объект содержит все поля журнала:
MESSAGE— текст записи журналаPRIORITY— числовой приоритет (0–7)_SYSTEMD_UNIT— исходный модуль__REALTIME_TIMESTAMP— микросекунды с начала эпохи_PID,_UID,_HOSTNAME— метаданные процесса
Этот поток NDJSON можно передать в jq, чтобы извлечь, отфильтровать или переформатировать поля для последующих систем оповещения, таких как PagerDuty, веб-перехватчики Slack или приёмники SIEM.
#!/usr/bin/env bash
# Extract error messages as a clean list for a Slack notification
MESSAGES=$(journalctl --no-pager \
-u nginx.service \
-p emerg..err \
--since "30 minutes ago" \
-o json \
| jq -r '.MESSAGE' \
| sort -u)
if [[ -n "${MESSAGES}" ]]; then
echo "Errors detected:"
echo "${MESSAGES}"
fiПросмотр журнала в реальном времени
Флаг -f заставляет journalctl отслеживать журнал в реальном времени — аналогично tail -f для файла журнала. В сочетании с фильтрами по модулю и приоритету это превращается в целевой мониторинг в реальном времени.
В сценариях обработки данных более полезен подход с опросом по курсору: сохраните текущий курсор журнала, а при каждом опросе передавайте --after-cursor=<cursor>, чтобы читать только новые записи с момента последней проверки. Это предотвращает повторную обработку старых строк.
Получите последний курсор с помощью --show-cursor -n 0 и разберите строку -- cursor: из вывода.
#!/usr/bin/env bash
# Cursor-based polling: read only new journal entries each run
CURSOR_FILE="/tmp/triage_cursor"
if [[ -f "${CURSOR_FILE}" ]]; then
CURSOR=$(cat "${CURSOR_FILE}")
NEW_ENTRIES=$(journalctl --no-pager \
-u myapp.service \
-p emerg..err \
--after-cursor="${CURSOR}" \
-o json)
else
# First run: look back 5 minutes
NEW_ENTRIES=$(journalctl --no-pager \
-u myapp.service \
-p emerg..err \
--since "5 minutes ago" \
-o json)
fi
# Save updated cursor for next poll
journalctl --no-pager -n 0 --show-cursor 2>&1 \
| grep '^-- cursor:' \
| sed 's/-- cursor: //' \
> "${CURSOR_FILE}"
echo "${NEW_ENTRIES}" | jq -r '.MESSAGE // empty'Запросы в пределах загрузки с помощью -b
Флаг -b ограничивает запрос определённым сеансом загрузки. Это особенно важно после сбоя или неожиданной перезагрузки: так можно получить журналы предыдущей загрузки, а не текущей.
-b 0— текущая загрузка (по умолчанию)-b -1— предыдущая загрузка-b -2— загрузка двумя перезапусками ранее--list-boots— показать все сохранённые сеансы загрузки с метками времени
Сценарии посмертного анализа обычно выгружают критические журналы предыдущей загрузки (-b -1), чтобы выяснить, почему система аварийно завершила работу или служба не запустилась.
#!/usr/bin/env bash
# Post-mortem: collect critical logs from the previous boot
echo "=== Previous boot sessions ==="
journalctl --list-boots
echo ""
echo "=== Critical entries from previous boot ==="
journalctl --no-pager \
-b -1 \
-p emerg..crit \
-o short-isoПоиск с помощью grep внутри journalctl и встроенные совпадения
После всех флагов можно передать обычный шаблон grep, однако journalctl также поддерживает встроенный поиск по полям с использованием синтаксиса FIELD=value. Встроенный поиск выполняется по структурированным метаданным и намного быстрее последующей обработки текста с помощью grep.
Часто используемые варианты:
_PID=1234— журналы определённого процесса_COMM=python3— журналы любого процесса с именемpython3SYSLOG_IDENTIFIER=myapp— журналы с пользовательским идентификатором
Несколько аргументов FIELD=value объединяются оператором AND; знак + между ними создаёт оператор OR. Используйте -g <regex> для полнотекстового поиска с помощью grep, если структурированных метаданных недостаточно.
#!/usr/bin/env bash
# Native field match: errors from the postgres process only
journalctl --no-pager \
_COMM=postgres \
-p emerg..err \
--since "1 hour ago"
# Full-text grep for a specific error string
journalctl --no-pager \
-u postgresql.service \
--since "1 hour ago" \
-g "FATAL|PANIC" \
--output=catСоздание многоразовой функции сортировки инцидентов
Освоив отдельные флаги, объедините их в многоразовую функцию Bash — это сделает сценарии сортировки инцидентов чистыми и единообразными. Хорошо спроектированная функция должна:
- принимать модуль, диапазон приоритетов и временной интервал в качестве параметров;
- использовать безопасные значения с низким уровнем шума, если аргументы не указаны;
- возвращать ненулевой код завершения при обнаружении ошибок (для интеграции с конвейерами CI);
- записывать результаты и в stdout, и в файл журнала с меткой времени для аудита.
#!/usr/bin/env bash
# triage.sh — reusable journal triage function
triage_unit() {
local unit="${1:?unit required}"
local priority="${2:-emerg..err}"
local since="${3:-1 hour ago}"
local logfile="/tmp/triage_${unit//[^a-zA-Z0-9]/_}_$(date +%s).log"
echo "[$(date -Iseconds)] Triaging ${unit} | prio=${priority} | since='${since}'" | tee "${logfile}"
journalctl --no-pager \
-u "${unit}" \
-p "${priority}" \
--since "${since}" \
-o short-iso \
| tee -a "${logfile}"
local count
count=$(wc -l < "${logfile}")
# Subtract 1 for the header line
(( count-- ))
if [[ "${count}" -gt 0 ]]; then
echo "[ALERT] ${count} line(s) logged to ${logfile}" >&2
return 1
fi
return 0
}
# Example: triage nginx errors in the last 30 minutes
triage_unit nginx.service "emerg..err" "30 minutes ago"Сценарий автоматической сортировки инцидентов
Следующий полный сценарий объединяет все концепции в практический инструмент автоматической сортировки инцидентов. Он читает список критически важных служб, запрашивает журнал каждой из них за настраиваемый период, обобщает результаты и завершается с кодом ошибки, если обнаружены какие-либо ошибки. Поэтому его можно использовать как задание cron или этап проверки состояния в CI.
#!/usr/bin/env bash
# incident_triage.sh — automated multi-service journal triage
set -euo pipefail
LOOKBACK="${1:-15 minutes ago}"
PRIORITY="emerg..err"
SERVICES=(nginx.service postgresql.service myapp-api.service myapp-worker.service)
REPORT="/tmp/incident_report_$(date +%Y%m%d_%H%M%S).txt"
FAILED=0
{
echo "Incident Triage Report"
echo "Generated : $(date -Iseconds)"
echo "Lookback : ${LOOKBACK}"
echo "Priority : ${PRIORITY}"
echo "-----------------------------------"
} > "${REPORT}"
for svc in "${SERVICES[@]}"; do
ENTRIES=$(journalctl --no-pager \
-u "${svc}" \
-p "${PRIORITY}" \
--since "${LOOKBACK}" \
--output=cat 2>/dev/null || true)
COUNT=$(echo "${ENTRIES}" | grep -c . || true)
if [[ "${COUNT}" -gt 0 ]]; then
echo "[FAIL] ${svc}: ${COUNT} error(s)" | tee -a "${REPORT}"
echo "${ENTRIES}" >> "${REPORT}"
echo "-----------------------------------" >> "${REPORT}"
FAILED=1
else
echo "[OK] ${svc}"
fi
done
echo ""
echo "Full report: ${REPORT}"
exit "${FAILED}"Проверка знаний: фильтрация по диапазону приоритетов
Вы пишете сценарий, который должен уведомлять дежурных инженеров только тогда, когда служба записывает сообщения с уровнем ошибки или более высоким уровнем серьёзности (то есть error, critical, alert или emergency). Какая комбинация флагов journalctl правильно выбирает именно этот диапазон?
Итоги урока: journalctl в сценариях
В этом уроке Вы узнали, как программно запрашивать журнал systemd для автоматической сортировки инцидентов:
- Всегда передавайте
--no-pagerв сценариях, чтобы предотвратить интерактивную блокировку. - Фильтрация по модулю (
-u) ограничивает запрос одной или несколькими службами; поддерживаются шаблоны и несколько флагов-u. - Фильтрация по приоритету (
-p emerg..err) выбирает только нужные уровни серьёзности — помните, что меньшие числа соответствуют большей серьёзности. - Временные интервалы (
--since/--until) ограничивают вывод журнала периодом развёртывания или периодом ретроспективного просмотра, используя метки времени в удобном для человека формате. - Ограничение по загрузке (
-b -1) позволяет сценариям посмертного анализа читать журналы предыдущего сеанса, завершившегося сбоем. - Вывод JSON (
-o json) иjqпозволяют создавать структурированные конвейеры для систем оповещения или SIEM. - Опрос по курсору с помощью
--after-cursorпредотвращает повторную обработку старых записей при последующих запусках. - Встроенный поиск по полям (
_COMM=,SYSLOG_IDENTIFIER=) быстрее передачи данных вgrep.
Объединение этих флагов в многоразовой функции Bash даёт готовый к эксплуатации инструмент сортировки инцидентов, который легко интегрируется с cron, конвейерами CI и рабочими процессами оповещения дежурных.
Часто задаваемые вопросы
Урок «Запросы к journald с journalctl в скриптах» бесплатный?
Да — полный текст урока «Запросы к journald с journalctl в скриптах» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс DevOps Bootcamp, подпишись на CoddyKit PRO. Курс DevOps Bootcamp содержит 4 уроков всего.
Чему я научусь в уроке «Запросы к journald с journalctl в скриптах»?
Фильтруйте записи журнала systemd по модулю, приоритету и времени для автоматического анализа инцидентов Ты практикуешь DevOps Bootcamp с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать DevOps Bootcamp?
Предыдущий опыт не требуется. DevOps Bootcamp на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Запросы к journald с journalctl в скриптах»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке DevOps Bootcamp?
Да. Каждый урок DevOps Bootcamp включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Масштабный разбор веб-журналов и журналов приложений
- Отслеживание журналов в реальном времени и потоковые оповещения
- Запросы к journald с journalctl в скриптах
- Вычисление показателей и гистограмм из потоков журналов