0Pricing
DevOps Bootcamp · Урок

Запросы к 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 — emerg
  • 1 — alert
  • 2 — crit
  • 3 — err
  • 4 — warning
  • 5 — notice
  • 6 — info
  • 7 — 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 — журналы любого процесса с именем python3
  • SYSLOG_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 — локальная установка не требуется.

Все уроки этого курса

  1. Масштабный разбор веб-журналов и журналов приложений
  2. Отслеживание журналов в реальном времени и потоковые оповещения
  3. Запросы к journald с journalctl в скриптах
  4. Вычисление показателей и гистограмм из потоков журналов
← Назад к DevOps Bootcamp