JSONデータの構造化集計:jqのgroup_byとreduceを用いた高度なログ分析の自動化

Tech

テーマ:jqを用いたJSON配列の高度な集計(group_by/reduce)。 構成:メタデータ、免責、H1、導入、フロー、実装、検証、トラブルシューティング、まとめ。 トーン:プロフェッショナルなSRE視点。正確、簡潔、堅牢。 技術的焦点:set -euo pipefail、trapによる一時ファイル削除、jqの効率的なパイプライン、systemd連携。

本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。

JSONデータの構造化集計:jqのgroup_byとreduceを用いた高度なログ分析の自動化

【導入と前提】 クラウドAPIやログから得た膨大なJSON配列を特定キーで集計し、統計値を算出する処理を堅牢なスクリプトで自動化します。

  • OS: GNU/Linux (Ubuntu 22.04+, RHEL 8+)

  • Tool: jq 1.6+, curl, bash 4.4+

【処理フローと設計】

graph TD
A["JSON Input: Raw Array"] --> B{Validation}
B -->|Valid| C["jq: group_by / reduce"]
B -->|Invalid| E["Error Handling"]
C --> D["Aggregation: Sum/Count/Average"]
D --> F["Final Output: Structured JSON/CSV"]

この設計では、まず入力データの整合性を確認し、group_byでデータを分類、あるいはreduceでメモリ効率良く集計処理を行い、最終的なレポートを生成します。

【実装:堅牢な自動化スクリプト】 以下は、HTTPアクセスログのJSON(例:status, response_time)を想定し、ステータスコードごとに件数と平均応答時間を算出するスクリプトです。

#!/usr/bin/env bash

# --- 実行環境の安全性確保 ---

set -euo pipefail

# -e: エラー発生時に即座に終了


# -u: 未定義変数の参照を禁止


# -o pipefail: パイプライン途中のエラーを捕捉

# --- トラップ処理(一時ファイルの確実な削除) ---

TMP_FILE=$(mktemp)
readonly TMP_FILE
trap 'rm -f "$TMP_FILE"' EXIT

# --- 擬似データの作成(本来はcurlやログファイルから取得) ---

cat << 'EOF' > "$TMP_FILE"
[
  {"status": 200, "latency": 120, "path": "/api/v1/user"},
  {"status": 500, "latency": 450, "path": "/api/v1/order"},
  {"status": 200, "latency": 150, "path": "/api/v1/user"},
  {"status": 404, "latency": 10, "path": "/favicon.ico"},
  {"status": 200, "latency": 130, "path": "/api/v1/auth"}
]
EOF

echo "--- [1] group_by を用いたグループ化と集計 ---"

# group_by(.key) は一度ソートするため、読みやすさを重視する場合に適する

jq -r '
  group_by(.status) 
  | map({
      status: .[0].status,
      count: length,
      avg_latency: (map(.latency) | add / length)
    })
' "$TMP_FILE"

echo "--- [2] reduce を用いたメモリ効率の良い集計 ---"

# reduce は大規模データにおいてソートのオーバーヘッドを避ける場合に適する

jq -r '
  reduce .[] as $item ({}; 
    .[$item.status|tostring] |= (
      .count += 1 | 
      .total_latency += $item.latency
    )
  ) 
  | to_entries 
  | map({
      status: .key,
      count: .value.count,
      avg_latency: (.value.total_latency / .value.count)
    })
' "$TMP_FILE"

# --- 定期実行のための systemd ユニット例 ---


# 以下の設定を /etc/systemd/system/json-aggregator.timer に配置することで


# 1時間ごとの定期集計が可能

: << 'SYSTEMD_TIMER'
[Unit]
Description=Run JSON Aggregation hourly

[Timer]
OnCalendar=hourly
Persistent=true

[Install]
WantedBy=timers.target
SYSTEMD_TIMER

【検証と運用】

  1. 正常系の確認: スクリプトを実行し、各ステータスコード(200, 404, 500)ごとに正しいcountavg_latencyが出力されているか確認します。

    ./aggregate.sh
    
  2. ログ確認 (systemd利用時): 定期実行の成否は以下のコマンドで確認可能です。

    journalctl -u json-aggregator.service -f
    

【トラブルシューティングと落とし穴】

  • 巨大なJSONによるメモリ不足: group_byは入力配列をすべてメモリ上に保持し、かつソートを行います。数GB単位のJSONを扱う場合は、jq -creduceを組み合わせるか、jqのストリーミングモード(--stream)の検討が必要です。

  • データ型の不一致: .status が文字列と数値で混在している場合、group_byの結果が分かれる可能性があります。集計前に tostringtonumber で正規化してください。

  • 権限設定: 一時ファイル(mktemp)が /tmp に作成できない、または trap が実行される前にプロセスが SIGKILL された場合の残存ファイルに注意してください。

【まとめ】 運用の冪等性と堅牢性を維持するための3つのポイント:

  1. 型の明示的な正規化: group_byreduce のキーには必ず tostring 等を適用し、予期せぬグループの分裂を防ぐ。

  2. パイプラインエラーの捕捉: Bashの set -o pipefail を活用し、curl の失敗や jq のパースエラーを無視せず停止させる。

  3. リソース制約の考慮: 大規模データには reduce を優先し、メモリ消費を抑える構造化を行う。

ライセンス:本記事のテキスト/コードは特記なき限り CC BY 4.0 です。引用の際は出典URL(本ページ)を明記してください。
利用ポリシー もご参照ください。

コメント

タイトルとURLをコピーしました