0Pricing
Linux Command Line & Bash Scripting Mastery · レッスン

スクリプトでjournalctlを使ったjournaldの検索

ユニット、優先度、時刻でsystemdのジャーナルエントリを絞り込み、インシデントの自動初動調査に役立てます。

「スクリプトでjournalctlを使ったjournaldの検索」はCoddyKit上の無料Linux Command Line & Bash Scripting Masteryレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはLinux Command Line & Bash Scripting Mastery学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Linux Command Line & Bash Scripting Masteryコースには全4レッスンが含まれています。

インシデントのトリアージにjournaldを使う理由

systemdを実行する最新のLinuxシステムでは、すべてのログ出力をジャーナルに集約します。ジャーナルはsystemd-journaldが管理する、構造化されたバイナリログストアです。/var/logにある通常のテキストファイルとは異なり、各ジャーナルエントリにはユニット名、優先度レベル、PID、UID、タイムスタンプなどの豊富なメタデータが含まれます。

自動化されたインシデントトリアージスクリプトでは、このメタデータによって次のことが可能になります。

  • grepの連鎖なしで、ログを1つのサービスに絞り込む
  • 正確な時間範囲(直近15分、デプロイ以降など)にクエリの対象を限定する
  • ノイズを無視して、重要度の高いエラーだけを出力する
  • 構造化された出力をアラートパイプラインに直接渡す

これらすべてを利用するためのツールがjournalctlです。このレッスンでは、Bashスクリプト内でプログラムから操作する方法を学びます。

journalctlの基本的な呼び出し

journalctlの最も単純な形式では、ジャーナル全体を出力します。スクリプトでこの使い方をすることはほとんどありません。常に少なくとも1つのフィルターを追加してください。ここでは、組み合わせて使うことの多いオプションを示します。

  • -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オプションには、有効なユニット名を指定できます。複数回指定してユニットを組み合わせることもできます。これは、1つのアプリケーションが複数のサービス(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(1 行につき 1 つの JSON オブジェクト、NDJSON)または -o json-pretty(整形済み)を指定します。各オブジェクトには、ジャーナルのすべてのフィールドが含まれます。

  • MESSAGE — ログ本文
  • PRIORITY — 数値の優先度(0–7)
  • _SYSTEMD_UNIT — 発生元の unit
  • __REALTIME_TIMESTAMP — エポックからの経過時間(マイクロ秒)
  • _PID、_UID、_HOSTNAME — プロセスのメタデータ

この NDJSON ストリームを jq にパイプして、フィールドを抽出、フィルタリング、または再フォーマットできます。PagerDuty、Slack webhooks、SIEM ingestors などの下流のアラートシステムに渡すデータを整形する際に便利です。

#!/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 と同様です。unit フィルターや優先度フィルターと組み合わせることで、対象を絞ったリアルタイム監視ができます。

スクリプト化されたパイプラインでは、より実用的なパターンとしてカーソルベースのポーリングがあります。現在のジャーナルカーソルを保存し、各ポーリングで --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 — 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

journalctl 内での grep とネイティブマッチの比較

すべてのフラグの後に raw な grep パターンを渡せますが、journalctl は FIELD=value 構文を使ったネイティブフィールドマッチにも対応しています。ネイティブマッチは構造化メタデータに対して評価されるため、grep でテキストを後処理するよりもはるかに高速です。

よく使われるマッチは次のとおりです。

  • _PID=1234 — 特定のプロセスからのログ
  • _COMM=python3 — python3 という名前の任意のプロセスからのログ
  • SYSLOG_IDENTIFIER=myapp — カスタム識別子が付いたログ

複数の FIELD=value 引数は AND 条件になります。引数の間に + を置くと OR 条件になります。構造化メタデータだけでは不十分な場合は、全文検索に -g <regex> を使用します。

#!/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 関数にまとめることで、トリアージスクリプトを簡潔かつ一貫性のあるものにできます。適切に設計された関数には、次の特性があります。

  • unit、優先度範囲、時間範囲をパラメーターとして受け取る
  • 引数が省略された場合は、安全でノイズの少ない値をデフォルトにする
  • エラーが見つかった場合は 0 以外の終了コードを返す(CI パイプラインと統合できる)
  • 監査証跡用に、検出結果を標準出力とタイムスタンプ付きログファイルの両方へ書き込む
#!/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"

インシデント自動トリアージスクリプト

次の完全なスクリプトは、ここまでの概念を実用的な自動トリアージツールにまとめたものです。重要なサービスの一覧を読み込み、設定可能な過去の検索期間について各サービスのジャーナルを検索し、検出結果を集約します。エラーが 1 つでも検出された場合は失敗コードで終了するため、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 journal をプログラムから検索する方法を学びました。

  • スクリプトでは必ず --no-pager を指定することで、対話操作によるブロックを防ぎます。
  • unit フィルタリング(-u)を使うと、1 つ以上のサービスにクエリの対象を限定できます。glob と複数の -u フラグにも対応しています。
  • 優先度フィルタリング(-p emerg..err)では、必要な重大度レベルだけを取得できます。数値が小さいほど重大である点に注意してください。
  • 時間範囲(--since / --until)では、人間が読みやすいタイムスタンプを使って、デプロイ期間や過去の検索期間にログ出力を限定できます。
  • ブートの限定(-b -1)により、ポストモーテムスクリプトで直前のクラッシュセッションのログを読み取れます。
  • JSON 出力(-o json)と jq により、アラートシステムや SIEM システムにデータを渡す構造化パイプラインを構築できます。
  • カーソルベースのポーリングで --after-cursor を使うと、繰り返し実行する際に古いエントリを再処理せずに済みます。
  • ネイティブフィールドマッチ(_COMM=、SYSLOG_IDENTIFIER=)は、grep へのパイプよりも高速です。

これらのフラグを再利用可能な Bash 関数に組み込むと、cron、CI パイプライン、オンコールアラートのワークフローにスムーズに統合できる、本番環境向けのトリアージツールになります。

よくある質問

「スクリプトでjournalctlを使ったjournaldの検索」レッスンは無料ですか?

はい。「スクリプトでjournalctlを使ったjournaldの検索」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Linux Command Line & Bash Scripting Masteryコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Linux Command Line & Bash Scripting Masteryコースには全4レッスンが含まれています。

「スクリプトでjournalctlを使ったjournaldの検索」で何を学びますか?

ユニット、優先度、時刻でsystemdのジャーナルエントリを絞り込み、インシデントの自動初動調査に役立てます。 ブラウザで直接実行するハンズオンコードでLinux Command Line & Bash Scripting Masteryを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Linux Command Line & Bash Scripting Masteryを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのLinux Command Line & Bash Scripting Masteryは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「スクリプトでjournalctlを使ったjournaldの検索」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このLinux Command Line & Bash Scripting Masteryレッスンでコードを書いて実行できますか?

はい。すべてのLinux Command Line & Bash Scripting Masteryレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. 大規模なWeb・アプリケーションログの解析
  2. リアルタイムのログ追跡とストリーミングアラート
  3. スクリプトでjournalctlを使ったjournaldの検索
  4. ログストリームからのメトリクスとヒストグラムの計算
← Linux Command Line & Bash Scripting Masteryに戻る