本記事は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
【検証と運用】
正常系の確認: スクリプトを実行し、各ステータスコード(200, 404, 500)ごとに正しい
countとavg_latencyが出力されているか確認します。./aggregate.sh
ログ確認 (systemd利用時): 定期実行の成否は以下のコマンドで確認可能です。
journalctl -u json-aggregator.service -f
【トラブルシューティングと落とし穴】
巨大なJSONによるメモリ不足:
group_byは入力配列をすべてメモリ上に保持し、かつソートを行います。数GB単位のJSONを扱う場合は、jq -cとreduceを組み合わせるか、jqのストリーミングモード(--stream)の検討が必要です。データ型の不一致:
.statusが文字列と数値で混在している場合、group_byの結果が分かれる可能性があります。集計前にtostringやtonumberで正規化してください。権限設定: 一時ファイル(
mktemp)が/tmpに作成できない、またはtrapが実行される前にプロセスがSIGKILLされた場合の残存ファイルに注意してください。
【まとめ】 運用の冪等性と堅牢性を維持するための3つのポイント:
型の明示的な正規化:
group_byやreduceのキーには必ずtostring等を適用し、予期せぬグループの分裂を防ぐ。パイプラインエラーの捕捉: Bashの
set -o pipefailを活用し、curlの失敗やjqのパースエラーを無視せず停止させる。リソース制約の考慮: 大規模データには
reduceを優先し、メモリ消費を抑える構造化を行う。

