{ “category”: “DevOps/SRE”, “topic”: “Advanced JSON Processing with jq”, “tools”: [“jq”, “bash”, “curl”], “complexity”: “Intermediate/Advanced”, “keywords”: [“group_by”, “reduce”, “aggregation”, “log-analysis”] } 本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。
JSONログの高度な集計自動化:jqを用いたグルーピングと統計レポートの生成
【導入と前提】 APIレスポンスや構造化ログから特定のキーを抽出し、出現頻度や合計値を高速に集計するSRE向けログ解析パイプラインの構築。
実行環境:GNU/Linux (Ubuntu/RHEL)
必須ツール:jq 1.6以上, curl
【処理フローと設計】
graph TD A["JSON Data Source"] -->|curl/cat| B["jq: Pre-filter"] B -->|group_by| C["jq: Grouping"] C -->|map/length| D["Summary Report"] B -->|reduce| E["Custom Aggregation"] D --> F["Final Output/Alert"] E --> F
group_by は指定したキーで配列を多次元配列に分割し、map と組み合わせることでカウントや平均値を算出します。一方、reduce はメモリ効率が良く、単一のパスで複雑なハッシュ集計(合計値計算など)を行う際に適しています。
【実装:堅牢な自動化スクリプト】
#!/usr/bin/env bash
# --- 安全のための設定 ---
set -euo pipefail
IFS=$'\n\t'
# --- 終了時のクリーンアップ ---
trap 'rm -f /tmp/raw_data.json' EXIT
# --- 設定:サンプルAPIエンドポイント ---
LOG_URL="https://api.example.com/v1/status/logs"
RAW_FILE="/tmp/raw_data.json"
echo "Step 1: ログデータの取得"
# curlオプション: -s (静音), -L (リダイレクト追従), --retry (一時エラー時の再試行)
if ! curl -sL --retry 3 "$LOG_URL" -o "$RAW_FILE"; then
echo "Error: データの取得に失敗しました。" >&2
exit 1
fi
echo "Step 2: group_by によるHTTPステータスコード別集計"
# group_by(.status): 指定キーでグループ化
# map({status: .[0].status, count: length}): 各グループの要素数をカウント
jq -r '
group_by(.status)
| map({
status: .[0].status,
count: length
})
| .[]
| "Status: \(.status) \t Count: \(.count)"
' "$RAW_FILE"
echo "Step 3: reduce による特定ユーザーのペイロード合計計算"
# reduce: 配列を一つずつ処理して accumulator (acc) に格納
# .[]: 入力配列を展開
# acc[.user] += .bytes: ユーザーごとのバイト数を累積
jq -r '
reduce .[] as $item ({}; .[$item.user] += $item.bytes)
| to_entries
| .[]
| "User: \(.key) \t Total Bytes: \(.value)"
' "$RAW_FILE"
# --- systemd-timer用の終了処理 ---
echo "処理が正常に完了しました。"
【検証と運用】
正常系の確認 スクリプトを実行し、標準出力にステータスコードとユーザーごとの集計が表示されることを確認します。
./analyze_logs.sh
ログの確認(cron/systemd経由の場合) systemdユニットとして登録している場合は、以下のコマンドで実行履歴を確認します。
journalctl -u json-aggregator.service --since "1 hour ago"
【トラブルシューティングと落とし穴】
巨大なJSONファイルの扱い:
jqは入力ファイルを一度メモリに読み込みます。数GB単位のログを処理する場合は、jq --streamオプションの使用を検討してください。データ型の不一致:
reduce内での加算処理(+=)は、初期値がnullだとエラーになる場合があります。($item.bytes // 0)のようにデフォルト値を設定すると安全です。環境変数の管理: APIキーなどの機密情報を含む場合は、スクリプト内にハードコードせず
export API_KEY=$(vault read ...)等のセキュアな方法で注入してください。一時ファイルの競合:
/tmpへの書き出しは、同時実行される可能性がある場合mktempを使用してファイル名の衝突を避けてください。
【まとめ】 運用の冪等性と堅牢性を維持するための3つのポイント:
パイプラインの保護:
set -o pipefailを使用し、jq内のエラーでスクリプトが停止するように設計する。型安全性の確保:
jq内でデータが存在しない可能性を考慮し、デフォルト値演算子(//)を適切に活用する。リソース管理:
trapによる確実な一時ファイル削除と、リトライ処理を含むcurlの利用を徹底する。

