0Pricing
DevOps Bootcamp · レッスン

ログストリームからのメトリクスとヒストグラムの計算

ストリーミング中のログデータから、リクエストレート、パーセンタイル、上位N件のレポートを直接集計します。

「ログストリームからのメトリクスとヒストグラムの計算」はCoddyKit上の無料DevOps Bootcampレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはDevOps Bootcamp学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 DevOps Bootcampコースには全4レッスンが含まれています。

raw ログからメトリクスを計算する理由

本番システムは、1 秒あたり数千行のログを出力します。raw ログを高価な分析プラットフォームへ送る代わりに、シェル上でリクエストレート、パーセンタイル、Top-N レポートを直接計算すれば、ほぼコストをかけずに済みます。

  • リクエストレート: サービスは 1 秒または 1 分あたりに何件のリクエストを処理していますか。
  • レイテンシのパーセンタイル: p50/p95/p99 の応答時間はどのくらいですか。
  • Top-N レポート: エンドポイント、IP、エラーコードのうち、最も頻繁に現れるものはどれですか。

awk、sort、uniq、bc などのシェルツールは、強力で組み合わせ可能なパイプラインを構成します。これにより、ターミナルを離れることなく、ライブのログストリームや過去のファイルからこれらの疑問に答えられます。

一般的なアクセスログの構造

ほとんどの Web サーバーはCombined Log Formatでログを書き込みます。各フィールドを理解することは、あらゆるメトリクスパイプラインの基礎です。

127.0.0.1 - frank [10/Oct/2024:13:55:36 -0700] "GET /api/users HTTP/1.1" 200 2326 0.042
  • フィールド 1: クライアント IP
  • フィールド 4(角括弧内): タイムスタンプ
  • フィールド 7(引用符内): HTTP メソッド + パス
  • フィールド 9: ステータスコード
  • フィールド 10: レスポンスのバイト数
  • フィールド 11: 応答時間(秒単位。カスタムフィールドで、常に存在するとは限りません)

awk を使うと、位置($1、$9 など)でフィールドを参照できます。引用符内のフィールドは、注意して分割した場合にのみ 1 つのトークンとして扱われます。ログ行を awk -F'"' で囲むか、複数回に分けて処理してください。

1 分あたりのリクエストレートのカウント

1 分あたりのリクエスト数を計算するには、各タイムスタンプから分の部分を取り出し、出現回数を数えます。[day/Mon/year:HH:MM というパターンで、時刻の分単位のバケットを取得できます。

次のパイプラインは access.log を読み込み、分 → リクエスト数の表を出力します。

#!/usr/bin/env bash
# Requests per minute from an nginx/apache access log
# Usage: bash req_per_min.sh access.log

LOG="${1:-access.log}"

awk '{
    # Extract [day/Mon/year:HH:MM from field 4
    match($0, /\[([^:]+:[0-9]+:[0-9]+)/, arr)
    minute = arr[1]
    if (minute != "") count[minute]++
} END {
    for (m in count) print count[m], m
}' "$LOG" | sort -k2

ライブストリームによるスライディングウィンドウのレート

ライブ追跡では、スライディングウィンドウが必要です。ここでのポイントは、tail -f の出力を awk にパイプし、システムクロック(systime())を使って N 秒ごとにカウンターをリセットすることです。

次の例では、10 秒ごとにレート行を出力します。

#!/usr/bin/env bash
# Live request rate — prints lines/10s from a tailed log
# Usage: bash live_rate.sh /var/log/nginx/access.log

LOG="${1:-/var/log/nginx/access.log}"
WINDOW=10

tail -f "$LOG" | awk -v win="$WINDOW" '
BEGIN { start = systime(); count = 0 }
{
    count++
    now = systime()
    if (now - start >= win) {
        printf "[%s] %d req/%ds (%.1f req/s)\n",
               strftime("%H:%M:%S", now), count, win, count/win
        count = 0
        start = now
    }
}'

パーセンタイル計算用のレイテンシ値の抽出

パーセンタイルを求めるには、観測したすべてのレイテンシ値をソートする必要があります。標準的な手順は次のとおりです。

  1. レイテンシ列を通常の数値リストとして抽出します。
  2. 数値順にソートします。
  3. 行数の計算を使って、正しい順位の値を選びます。

次のスクリプトは、フィールド 11(秒単位の応答時間)を抽出し、次の手順でパーセンタイルを計算するために一時ファイルへ保存します。

#!/usr/bin/env bash
# Extract latency column from access log (field 11)
# Assumes last field on each line is response time in seconds

LOG="${1:-access.log}"
TMP=$(mktemp /tmp/latency_XXXXXX.txt)

awk '{ if ($NF ~ /^[0-9]+\.?[0-9]*$/) print $NF }' "$LOG" \
    | sort -n > "$TMP"

echo "Extracted $(wc -l < "$TMP") latency samples -> $TMP"
echo "$TMP"   # callers can read this file

awk による p50、p95、p99 の計算

レイテンシ値をソートしたら、パーセンタイルの選択は算術計算で行えます。p パーセンタイルは、行 ceil(p/100 * N) に位置します。ソート済みファイルを配列に読み込めば、純粋な awk だけで 1 回の走査で計算できます。

#!/usr/bin/env bash
# Compute p50 / p95 / p99 from a sorted latency file
# Usage: bash percentiles.sh latency_sorted.txt

# Demo: generate 1000 random latencies if no file given
if [[ $# -eq 0 ]]; then
    SORTED=$(mktemp)
    for i in $(seq 1 1000); do
        awk 'BEGIN { srand(); printf "%.4f\n", 0.001 + rand()*0.999 }'
    done | sort -n > "$SORTED"
else
    SORTED="$1"
fi

awk '
{ values[NR] = $1 }
END {
    n = NR
    if (n == 0) { print "No data"; exit }
    p50  = values[int(n * 0.50 + 0.9999)]
    p95  = values[int(n * 0.95 + 0.9999)]
    p99  = values[int(n * 0.99 + 0.9999)]
    printf "p50  = %.4fs\np95  = %.4fs\np99  = %.4fs\nN    = %d\n",
           p50, p95, p99, n
}' "$SORTED"

Top-N エンドポイントレポートの作成

Top-N レポートでは、「最も多くアクセスされたパスはどれか」という疑問に答えられます。シェルでよく使われる定石は次のとおりです。

  • awk で対象のフィールド(例: URL パス)を出力する
  • sort で同じ値をまとめる
  • uniq -c で連続する重複を数える
  • sort -rn で件数の降順に順位付けする
  • head -n N で上位 N 件を取得する
#!/usr/bin/env bash
# Top-10 most requested URL paths from access log
# Usage: bash top_endpoints.sh access.log [N]

LOG="${1:-access.log}"
N="${2:-10}"

echo "=== Top $N endpoints ==="
awk -F'"' '{ print $2 }' "$LOG" \
    | awk '{ print $2 }' \
    | sort \
    | uniq -c \
    | sort -rn \
    | head -n "$N" \
    | awk '{ printf "%6d  %s\n", $1, $2 }'

ステータスコード別 Top-N: エラーをひと目で把握

HTTP ステータスコードは、サービスの健全性を示します。ログ行をステータスコードごとにグループ化して数えると、エラーがまれに発生しているのか、システム全体に及んでいるのかが分かります。

次のスクリプトは、2xx/3xx/4xx/5xx のすべてのバケットについて内訳を生成します。

#!/usr/bin/env bash
# Status code frequency breakdown
# Usage: bash status_breakdown.sh access.log

LOG="${1:-access.log}"

echo "=== HTTP Status Code Distribution ==="
awk '{ print $9 }' "$LOG" \
    | grep -E '^[0-9]{3}$' \
    | sort \
    | uniq -c \
    | sort -rn \
    | awk '{ printf "%6d  %s\n", $1, $2 }'

echo
echo "=== 5xx Error Spike Check ==="
awk '$9 ~ /^5/ { print $9, $7 }' "$LOG" \
    | sort | uniq -c | sort -rn | head -20

awk による ASCII ヒストグラム

テキストヒストグラムを使うと、ターミナルや CI ログでレイテンシの分布をすぐに読み取れます。手順は次のとおりです。

  1. 各レイテンシ値をビン(例: 0-50ms、50-100ms、…)に分類します。
  2. ビンごとに観測数を数えます。
  3. 最大件数に合わせてスケーリングした # 文字の棒を出力します。

グラフ作成ツールを使わずに、二峰性の分布や外れ値を見つける際に非常に役立ちます。

#!/usr/bin/env bash
# ASCII latency histogram from a list of latency values in seconds
# Demo: generates synthetic data if no input file is provided

if [[ $# -eq 0 ]]; then
    # Generate 500 synthetic latencies (ms converted to s)
    python3 -c "
import random, math
for _ in range(500):
    # bimodal: fast cluster ~50ms, slow cluster ~300ms
    if random.random() < 0.75:
        v = max(1, random.gauss(50, 15))
    else:
        v = max(1, random.gauss(300, 80))
    print(f'{v/1000:.4f}')
" | sort -n | awk '
{ values[NR] = $1 * 1000 }  # convert to ms
END {
    buckets = 10; maxVal = 500
    step = maxVal / buckets
    for (i = 0; i < buckets; i++) hist[i] = 0
    for (i = 1; i <= NR; i++) {
        b = int(values[i] / step)
        if (b >= buckets) b = buckets - 1
        hist[b]++
    }
    maxCount = 0
    for (i = 0; i < buckets; i++) if (hist[i] > maxCount) maxCount = hist[i]
    print "Latency (ms)   Count   Distribution"
    print "---------------------------------------"
    for (i = 0; i < buckets; i++) {
        lo = i * step; hi = lo + step
        barLen = int(hist[i] / maxCount * 40)
        bar = ""
        for (j = 0; j < barLen; j++) bar = bar "#"
        printf "%4d-%4dms  %5d  %s\n", lo, hi, hist[i], bar
    }
}'
else
    echo "Usage: pipe a sorted latency file (in seconds) to this pattern"
fi

メトリクスの統合: 一括サマリーレポート

本番環境のランブックでは、レート、レイテンシのパーセンタイル、上位エンドポイント、エラーレートといった主要なメトリクスをすべて一度に出力する単一のコマンドが必要になることがよくあります。ここまでに学んだ内容を 1 つのスクリプトにまとめ、人間やアラートシステムから呼び出せるようにできます。

#!/usr/bin/env bash
# One-shot log summary report
# Usage: bash log_summary.sh access.log

LOG="${1:-access.log}"
[[ -f "$LOG" ]] || { echo "File not found: $LOG"; exit 1; }

TOTAL=$(wc -l < "$LOG")
ERRORS=$(awk '$9 ~ /^[45]/' "$LOG" | wc -l)
ERR_RATE=$(awk "BEGIN { printf \"%.1f\", ($ERRORS / ($TOTAL || 1)) * 100 }")

echo "====================================="
echo " Log Summary: $LOG"
echo "====================================="
printf " Total requests : %d\n" "$TOTAL"
printf " 4xx/5xx errors : %d (%.1f%%)\n" "$ERRORS" "$ERR_RATE"

echo
echo "--- Top 5 Endpoints ---"
awk -F'"' '{ print $2 }' "$LOG" | awk '{ print $2 }' \
    | sort | uniq -c | sort -rn | head -5 \
    | awk '{ printf "  %6d  %s\n", $1, $2 }'

echo
echo "--- Latency Percentiles ---"
awk '{ if ($NF ~ /^[0-9]+\.?[0-9]*$/) print $NF * 1000 }' "$LOG" \
    | sort -n \
    | awk '
{ v[NR]=$1 }
END {
  if (NR==0) { print "  No latency data"; exit }
  printf "  p50 = %.1fms\n", v[int(NR*0.50+0.9999)]
  printf "  p95 = %.1fms\n", v[int(NR*0.95+0.9999)]
  printf "  p99 = %.1fms\n", v[int(NR*0.99+0.9999)]
}'

名前付きパイプと tee によるストリーミングメトリクス

オブザーバビリティパイプラインでは、ログストリームを分岐する必要がよくあります。つまり、raw 行をディスクに書き込みながら、同時にメトリクスも計算します。名前付きパイプ(mkfifo)と tee を使えば、ストリーム全体をメモリにバッファリングせずにこれを実現できます。

  • mkfifo /tmp/log_pipe — 名前付きパイプを作成する
  • tee /tmp/log_pipe | metric_consumer & — 一方の分岐をメトリクスコンシューマーへ送る
  • もう一方の分岐でアーカイブファイルに書き込む

このパターンにより、ディスクへの書き込みとメトリクスの集計を分離でき、どちらの側も独立して再起動できます。

#!/usr/bin/env bash
# Fan-out: write to archive AND count errors in real time
# Run: bash fanout_pipeline.sh /var/log/nginx/access.log

LOG="${1:-/var/log/nginx/access.log}"
ARCHIVE="/tmp/access_archive.log"
PIPE="/tmp/log_metrics_pipe"

# Clean up on exit
trap 'rm -f "$PIPE"' EXIT

mkfifo "$PIPE"

# Branch 1: count 5xx errors per minute from the pipe
awk '$9 ~ /^5/ {
    match($0, /\[([^:]+:[0-9]+:[0-9]+)/, arr)
    errors[arr[1]]++
} END {
    for (m in errors) printf "5xx errors at %s: %d\n", m, errors[m]
}' "$PIPE" &

# Branch 2: archive to disk + feed Branch 1 via pipe
tail -f "$LOG" | tee "$PIPE" >> "$ARCHIVE"

ソート済み配列から p95 パーセンタイルを正しく計算する awk の手法はどれですか

N 個のソート済みレイテンシ値を awk の配列 v[1..N] に読み込んであります。p95 パーセンタイルを正しく取得する式はどれですか。

レッスンのまとめ: ログストリームからのメトリクスとヒストグラム

このレッスンでは、Bash だけで完全なメトリクスパイプラインを構築しました。

  • リクエストレート: awk でタイムスタンプから分単位のバケットを抽出して出現回数を数え、tail -f と systime() でライブのスライディングウィンドウレートを取得します。
  • レイテンシパーセンタイル: レイテンシ列を抽出し、sort -n でソートしてから、awk で int(N * p + 0.9999) を使い、ceil(p/100 * N) 番目の行を選びます。
  • Top-N レポート: 定番の awk | sort | uniq -c | sort -rn | head -N パイプラインは、パス、IP、ステータスコードなど、あらゆるカテゴリフィールドで利用できます。
  • ASCII ヒストグラム: 値をバケットに分け、ビンごとに数を集計し、最大のビンの件数に合わせて棒の長さを拡大縮小することで、分布をひと目で確認できます。
  • 名前付きパイプによるファンアウト: mkfifo と tee を使うと、ストリーム全体をメモリに読み込まずに、生ログのアーカイブとメトリクスコンシューマーへの供給を同時に行えます。

これらの組み合わせ可能なプリミティブにより、インシデント発生時に外部ツールへ依存する必要がなくなり、Bash が動作するあらゆる環境で実行できる軽量なオブザーバビリティスクリプトの基盤となります。

よくある質問

「ログストリームからのメトリクスとヒストグラムの計算」レッスンは無料ですか?

はい。「ログストリームからのメトリクスとヒストグラムの計算」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、DevOps Bootcampコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 DevOps Bootcampコースには全4レッスンが含まれています。

「ログストリームからのメトリクスとヒストグラムの計算」で何を学びますか?

ストリーミング中のログデータから、リクエストレート、パーセンタイル、上位N件のレポートを直接集計します。 ブラウザで直接実行するハンズオンコードでDevOps Bootcampを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

DevOps Bootcampを始めるのに経験は必要ですか?

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

「ログストリームからのメトリクスとヒストグラムの計算」レッスンにはどのくらい時間がかかりますか?

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

このDevOps Bootcampレッスンでコードを書いて実行できますか?

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

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

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