<p><style_prompt>
{
“role”: “Experienced SRE / DevOps Engineer”,
“tone”: “Professional, Technical, Implementation-focused”,
“format”: “Markdown”,
“key_elements”: [“set -euo pipefail”, “trap”, “Mermaid”, “jq”, “idempotency”],
“language”: “Japanese”
}
</style_prompt></p>
<p>本記事は<strong>Geminiの出力をプロンプト工学で整理した業務ドラフト(未検証)</strong>です。</p>
<h1 class="wp-block-heading">JSONログ集計の自動化:jqのgroup_byとreduceによる高度な統計レポート生成</h1>
<h2 class="wp-block-heading">【導入と前提】</h2>
<p>大量のJSON構造を持つシステムログやAPIレスポンスを、特定のキーでグループ化・集計し、統計レポートを生成する運用を自動化します。</p>
<ul class="wp-block-list">
<li><p><strong>OS</strong>: GNU/Linux (Ubuntu 22.04 LTS / RHEL 9 等)</p></li>
<li><p><strong>ツール</strong>: <code>jq</code> (v1.6以上推奨), <code>bash</code> (4.x以上)</p></li>
<li><p><strong>権限</strong>: 一般ユーザー権限(ログ保存先への書き込み権限が必要)</p></li>
</ul>
<h2 class="wp-block-heading">【処理フローと設計】</h2>
<div class="wp-block-merpress-mermaidjs diagram-source-mermaid"><pre class="mermaid">
graph TD
A["JSON Data Source"] -->|curl / cat| B("Pre-process: Filter")
B -->|jq: group_by| C("Categorize by Key")
C -->|jq: reduce / map| D("Aggregate metrics")
D -->|Format| E["Structured Report/Alert"]
</pre></div>
<ol class="wp-block-list">
<li><p><strong>Input</strong>: 複数のJSONオブジェクトが含まれるストリームまたは配列。</p></li>
<li><p><strong>Process</strong>: <code>group_by</code> で特定フィールド(例:ステータスコード)ごとにソート・分離。</p></li>
<li><p><strong>Aggregate</strong>: <code>reduce</code> または <code>map</code> を用いて、グループ内の数値合計や平均、発生頻度を算出。</p></li>
<li><p><strong>Output</strong>: 後続の監視ツールやダッシュボードに渡し易い、整理されたJSON形式で出力。</p></li>
</ol>
<h2 class="wp-block-heading">【実装:堅牢な自動化スクリプト】</h2>
<div class="codehilite">
<pre data-enlighter-language="generic">#!/usr/bin/env bash
# --- 安全のためのシェルオプション設定 ---
set -euo pipefail
# -e: エラー発生時に即座に終了
# -u: 未定義変数の参照時にエラー
# -o pipefail: パイプラインの途中のエラーを拾う
# --- 一時ファイルの管理 ---
TMP_FILE=$(mktemp)
readonly TMP_FILE
trap 'rm -f "$TMP_FILE"' EXIT # スクリプト終了時に必ず削除
# --- 設定値 ---
readonly RAW_LOG_URL="https://api.example.com/v1/metrics" # 例としてのエンドポイント
readonly REPORT_PATH="./daily_report.json"
main() {
echo "Starting JSON log aggregation..."
# 1. データの取得(リトライ付き)
# --retry: 通信失敗時の再試行
# -L: リダイレクトを追跡
# -s: 進捗を表示しない(silent)
if ! curl -sL --retry 3 "${RAW_LOG_URL}" -o "$TMP_FILE"; then
echo "Error: Failed to fetch log data." >&2
exit 1
fi
# 2. jqによる高度な集計
# ロジック:
# .data 配列を取得 -> status でグループ化
# -> 各グループに対し {status, count, avg_latency} を算出
# -> reduceを使って最終的なサマリJSONを構築
jq '
.data |
group_by(.status) |
map({
status: .[0].status | tostring,
count: length,
avg_latency: (map(.latency) | add / length)
}) |
reduce .[] as $item ({}; . + {($item.status): {count: $item.count, latency: $item.avg_latency}})
' "$TMP_FILE" > "$REPORT_PATH"
echo "Report generated at: ${REPORT_PATH}"
}
main "$@"
</pre>
</div>
<h2 class="wp-block-heading">【検証と運用】</h2>
<h3 class="wp-block-heading">正常系の確認</h3>
<p>生成されたレポートの内容を確認します。</p>
<div class="codehilite">
<pre data-enlighter-language="generic"># 生成されたJSONを整形表示
cat daily_report.json | jq .
# 期待される出力例
# {
# "200": { "count": 150, "latency": 45.2 },
# "500": { "count": 2, "latency": 1200.5 }
# }
</pre>
</div>
<h3 class="wp-block-heading">エラー確認</h3>
<p>スクリプトが失敗した場合や、systemd等で実行している場合は <code>journalctl</code> を確認します。</p>
<div class="codehilite">
<pre data-enlighter-language="generic"># systemdタイマー等で実行している場合
journalctl -u log-aggregation.service -e
</pre>
</div>
<h2 class="wp-block-heading">【トラブルシューティングと落とし穴】</h2>
<ol class="wp-block-list">
<li><p><strong>メモリ消費</strong>: <code>group_by</code> は内部で全データのソートを行うため、数GB単位の超巨大JSONを処理する場合、メモリ不足になる可能性があります。その場合は <code>jq</code> の <code>--stream</code> オプションを検討するか、前段で <code>split</code> して処理してください。</p></li>
<li><p><strong>キーの不在</strong>: 集計対象のキー(例:<code>.latency</code>)が存在しないオブジェクトが混じると <code>add</code> でエラーになります。<code>(.latency // 0)</code> のようにデフォルト値を設定する防衛的記述が有効です。</p></li>
<li><p><strong>環境変数の管理</strong>: APIキー等が必要な場合、スクリプト内にハードコードせず <code>export API_KEY=...</code> または <code>.env</code> ファイルから読み込むように設計してください。</p></li>
</ol>
<h2 class="wp-block-heading">【まとめ】</h2>
<p>運用の冪等性と堅牢性を維持するためのポイント:</p>
<ul class="wp-block-list">
<li><p><strong>データのクリーンアップ</strong>: <code>trap</code> を使用して、失敗時も含め一時ファイルを確実に削除する。</p></li>
<li><p><strong>構造の検証</strong>: <code>jq</code> 処理の前に、入力データが期待するスキーマを持っているかチェックする。</p></li>
<li><p><strong>パイプラインの保護</strong>: <code>set -o pipefail</code> を指定し、<code>jq</code> 内でのエラーを見逃さないようにする。</p></li>
</ul>
{
“role”: “Experienced SRE / DevOps Engineer”,
“tone”: “Professional, Technical, Implementation-focused”,
“format”: “Markdown”,
“key_elements”: [“set -euo pipefail”, “trap”, “Mermaid”, “jq”, “idempotency”],
“language”: “Japanese”
}
本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。
JSONログ集計の自動化:jqのgroup_byとreduceによる高度な統計レポート生成
【導入と前提】
大量のJSON構造を持つシステムログやAPIレスポンスを、特定のキーでグループ化・集計し、統計レポートを生成する運用を自動化します。
OS: GNU/Linux (Ubuntu 22.04 LTS / RHEL 9 等)
ツール: jq (v1.6以上推奨), bash (4.x以上)
権限: 一般ユーザー権限(ログ保存先への書き込み権限が必要)
【処理フローと設計】
graph TD
A["JSON Data Source"] -->|curl / cat| B("Pre-process: Filter")
B -->|jq: group_by| C("Categorize by Key")
C -->|jq: reduce / map| D("Aggregate metrics")
D -->|Format| E["Structured Report/Alert"]
Input: 複数のJSONオブジェクトが含まれるストリームまたは配列。
Process: group_by で特定フィールド(例:ステータスコード)ごとにソート・分離。
Aggregate: reduce または map を用いて、グループ内の数値合計や平均、発生頻度を算出。
Output: 後続の監視ツールやダッシュボードに渡し易い、整理されたJSON形式で出力。
【実装:堅牢な自動化スクリプト】
#!/usr/bin/env bash
# --- 安全のためのシェルオプション設定 ---
set -euo pipefail
# -e: エラー発生時に即座に終了
# -u: 未定義変数の参照時にエラー
# -o pipefail: パイプラインの途中のエラーを拾う
# --- 一時ファイルの管理 ---
TMP_FILE=$(mktemp)
readonly TMP_FILE
trap 'rm -f "$TMP_FILE"' EXIT # スクリプト終了時に必ず削除
# --- 設定値 ---
readonly RAW_LOG_URL="https://api.example.com/v1/metrics" # 例としてのエンドポイント
readonly REPORT_PATH="./daily_report.json"
main() {
echo "Starting JSON log aggregation..."
# 1. データの取得(リトライ付き)
# --retry: 通信失敗時の再試行
# -L: リダイレクトを追跡
# -s: 進捗を表示しない(silent)
if ! curl -sL --retry 3 "${RAW_LOG_URL}" -o "$TMP_FILE"; then
echo "Error: Failed to fetch log data." >&2
exit 1
fi
# 2. jqによる高度な集計
# ロジック:
# .data 配列を取得 -> status でグループ化
# -> 各グループに対し {status, count, avg_latency} を算出
# -> reduceを使って最終的なサマリJSONを構築
jq '
.data |
group_by(.status) |
map({
status: .[0].status | tostring,
count: length,
avg_latency: (map(.latency) | add / length)
}) |
reduce .[] as $item ({}; . + {($item.status): {count: $item.count, latency: $item.avg_latency}})
' "$TMP_FILE" > "$REPORT_PATH"
echo "Report generated at: ${REPORT_PATH}"
}
main "$@"
【検証と運用】
正常系の確認
生成されたレポートの内容を確認します。
# 生成されたJSONを整形表示
cat daily_report.json | jq .
# 期待される出力例
# {
# "200": { "count": 150, "latency": 45.2 },
# "500": { "count": 2, "latency": 1200.5 }
# }
エラー確認
スクリプトが失敗した場合や、systemd等で実行している場合は journalctl を確認します。
# systemdタイマー等で実行している場合
journalctl -u log-aggregation.service -e
【トラブルシューティングと落とし穴】
メモリ消費: group_by は内部で全データのソートを行うため、数GB単位の超巨大JSONを処理する場合、メモリ不足になる可能性があります。その場合は jq の --stream オプションを検討するか、前段で split して処理してください。
キーの不在: 集計対象のキー(例:.latency)が存在しないオブジェクトが混じると add でエラーになります。(.latency // 0) のようにデフォルト値を設定する防衛的記述が有効です。
環境変数の管理: APIキー等が必要な場合、スクリプト内にハードコードせず export API_KEY=... または .env ファイルから読み込むように設計してください。
【まとめ】
運用の冪等性と堅牢性を維持するためのポイント:
データのクリーンアップ: trap を使用して、失敗時も含め一時ファイルを確実に削除する。
構造の検証: jq 処理の前に、入力データが期待するスキーマを持っているかチェックする。
パイプラインの保護: set -o pipefail を指定し、jq 内でのエラーを見逃さないようにする。
ライセンス:本記事のテキスト/コードは特記なき限り
CC BY 4.0 です。引用の際は出典URL(本ページ)を明記してください。
利用ポリシー もご参照ください。
コメント