Linux: フェーズ3ミニプロジェクト — サーバーログ分析

フェーズ3の総合プロジェクトです。リダイレクト、パイプ、grep、awk、sed、sort、uniqなどすべてのデータ処理スキルを組み合わせて、Nginxアクセスログの全次元分析を行いレポートを生成します。

📋 前提条件:まず以下を完了していること

1. このレッスンで学ぶこと


2. 小明のWebサイトが遅くなった

(1) 悩み:ボスがデータを要求、10GBのログは閲覧不可能

小明のWebサイトが最近遅く、ボスは「データを出せ」と要求しました。Nginxのaccess.logは毎日数百MB生成 — 開くのは不可能、キーワード検索も干し草の山から針を探すようなもの。

(2) パイプラインチェーン分析

小明はフェーズ3のスキルを使い、パイプコマンドでキーメトリクスを順次抽出しました:

BASH
# Top 10 IPs by access count
cat access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -10

# Top 10 most-requested URLs
cat access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -10

> 💡 ヒント:ログ分析のパイプラインパターンは固定されています:`awk フィールド抽出 → sort → uniq -c 重複削除+カウント → sort -rn 降順 → head 上位N件`。このテンプレートを覚えれば、あらゆるログのTop N分析に適用できます。

# Requests per hour
cat access.log | awk -F: '{print $2":00"}' | sort | uniq -c | sort -rn

出力:

TEXT
    210 192.168.1.100
    198 10.0.0.45
    195 192.168.1.200
    190 10.0.0.88
    180 172.16.0.50
    150 /index.html
    120 /api/users
    110 /api/login
    100 /about
     90 /contact
     85 10:00
     80 14:00
     75 09:00
     70 22:00
     65 18:00

(3) 得られるもの:5分でレポート完成

小明は分析結果をレポートにまとめました — アクセストレンド、遅いリクエストトップ10、404エラーが最も多いページ。ボスは言いました:「よし、サーバーを増やそう。」


3. 知識ポイント

(1) 標準Nginxログフォーマット(combined)

TEXT

192.168.1.100 - - [07/Jul/2026:10:15:30 +0000] "GET /index.html HTTP/1.1" 200 1234 "-" "Mozilla/5.0"
│              │ │ │                           │                           │    │    │
│              │ │ └─ User-Agent               │                           │    │    └─ Response bytes
│              │ └───────────────────────────── Referer                    │    └────── Status code
│              └─────────────────────────────────────────────────────────── Request line (method/URL/protocol)
└─── Client IP                                   Timestamp

ℹ️ 注意:Nginxのcombinedログフォーマットでは、フィールドはスペース区切りですが、一部のフィールド自体にスペースが含まれています(リクエスト行とタイムスタンプはブラケットと引用符で囲まれています)。awkがスペースで分割する場合、$1はIP、$7はURL、$9はステータスコード、$10はバイト数 — これらのフィールド位置は安定しており、awkで直接抽出できます。

フィールド位置(スペース区切り):

フィールド 意味
$1 クライアントIP
$4 タイムスタンプ [ 部分
$7 リクエストURL
$9 HTTPステータスコード
$10 レスポンスバイト数

(2) ▶ サンプル:サンプルログの生成

まず、分析用のサンプルログファイルを生成します:

TEXT
#!/bin/bash
# generate-sample-log.sh

IPS=("192.168.1.100" "10.0.0.45" "192.168.1.200" "10.0.0.88" "172.16.0.50")
URLS=("/index.html" "/api/users" "/api/login" "/about" "/contact" "/images/logo.png" "/api/orders" "/products" "/blog")
STATUSES=(200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 201 301 302 400 401 403 404 500 502 503)

rm -f /tmp/access.log

for i in {1..1000}; do
    IP=${IPS[$RANDOM % ${#IPS[@]}]}
    URL=${URLS[$RANDOM % ${#URLS[@]}]}
    STATUS=${STATUSES[$RANDOM % ${#STATUSES[@]}]}
    SIZE=$((RANDOM % 10000 + 100))
    HOUR=$((RANDOM % 24))
    MIN=$((RANDOM % 60))
    SEC=$((RANDOM % 60))
    echo "$IP - - [07/Jul/2026:$HOUR:$MIN:$SEC +0000] \"GET $URL HTTP/1.1\" $STATUS $SIZE \"-\" \"Mozilla/5.0\"" >> /tmp/access.log
done

echo "Generated 1000 sample log entries to /tmp/access.log"
BASH
chmod +x generate-sample-log.sh
./generate-sample-log.sh

出力:

TEXT
Generated 1000 sample log entries to /tmp/access.log

(3) ▶ サンプル:リクエスト量統計

BASH
echo "=== Total Requests ==="
wc -l /tmp/access.log

echo ""
echo "=== Requests per IP ==="
cat /tmp/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -10

echo ""
echo "=== Request Method Distribution ==="
cat /tmp/access.log | awk '{print $6}' | tr -d '"' | sort | uniq -c | sort -rn

出力:

TEXT
=== Total Requests ===
1000 /tmp/access.log

=== Requests per IP ===
    210 192.168.1.100
    198 10.0.0.45
    195 192.168.1.200
    190 10.0.0.88
    180 172.16.0.50

=== Request Method Distribution ===
   1000 GET

(4) ▶ サンプル:ステータスコード分析

BASH
echo "=== Status Code Distribution ==="
cat /tmp/access.log | awk '{print $9}' | sort | uniq -c | sort -rn

echo ""
echo "=== 4xx Errors (Client Errors) ==="
cat /tmp/access.log | awk '$9 ~ /^4/ {print $9, $7, $1}' | sort | uniq -c | sort -rn | head -10

echo ""
echo "=== 5xx Errors (Server Errors) ==="
cat /tmp/access.log | awk '$9 ~ /^5/ {print $9, $7, $1}' | sort | uniq -c | sort -rn | head -10

echo ""
echo "=== Error Rate ==="
TOTAL=$(wc -l < /tmp/access.log)
ERRORS=$(awk '$9 ~ /^[45]/ {count++} END {print count}' /tmp/access.log)
echo "Total requests: $TOTAL"
echo "Errors: $ERRORS"
echo "Error rate: $(echo "scale=2; $ERRORS * 100 / $TOTAL" | bc)%"

出力:

TEXT
=== Status Code Distribution ===
    960 200
      5 201
      3 301
      2 302
      8 400
      2 401
      3 403
     10 404
      4 500
      2 502
      1 503

=== 4xx Errors (Client Errors) ===
   3 404 /api/login 192.168.1.100
   2 404 /about 10.0.0.45
   2 403 /api/admin 192.168.1.200

=== 5xx Errors (Server Errors) ===
   2 500 /api/data 10.0.0.88
   2 502 /api/users 172.16.0.50
   1 503 /api/health 192.168.1.100

=== Error Rate ===
Total requests: 1000
Errors: 32
Error rate: 3.20%

(5) ▶ サンプル:URLホットスポット分析

BASH
echo "=== Most Popular URLs ==="
cat /tmp/access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -10

echo ""
echo "=== URLs with Most 404s ==="
cat /tmp/access.log | awk '$9 == 404 {print $7}' | sort | uniq -c | sort -rn | head -10

echo ""
echo "=== URLs with Largest Response Size ==="
cat /tmp/access.log | awk '{print $10, $7}' | sort -rn | head -10

出力:

TEXT
=== Most Popular URLs ===
    150 /index.html
    120 /api/users
    110 /api/login
    100 /about
     90 /contact

=== URLs with Most 404s === 5 /api/login 3 /old-page 2 /favicon.ico

=== URLs with Largest Response Size === 10098 /images/logo.png 9876 /api/users 8765 /api/orders 7654 /products 6543 /blog

(6) ▶ サンプル:時間次元分析

BASH
echo "=== Requests per Hour ==="
cat /tmp/access.log | awk -F: '{print $2":00"}' | sort | uniq -c | sort -rn

echo ""
echo "=== Peak Traffic Hours ==="
cat /tmp/access.log | awk -F: '{print $2}' | sort | uniq -c | sort -rn | head -5

echo ""
echo "=== Requests per Minute (Top 10) ==="
cat /tmp/access.log | awk -F: '{print $2":"$3}' | sort | uniq -c | sort -rn | head -10

出力:

TEXT
=== Requests per Hour ===
     85 10:00
     80 14:00
     75 09:00
     70 22:00
     65 18:00

=== Peak Traffic Hours ===
     85 10
     80 14
     75 09
     70 22
     65 18

=== Requests per Minute (Top 10) ===
      5 10:15
      4 14:30
      4 09:45
      3 22:10
      3 18:05

(7) ▶ サンプル:パフォーマンス分析

BASH
echo "=== Response Size Analysis (using $10 as simulated response size) ==="
echo "Largest response: $(awk '{print $10}' /tmp/access.log | sort -rn | head -1) bytes"
echo "Average response: $(awk '{sum += $10; count++} END {printf "%.0f", sum/count}' /tmp/access.log) bytes"
echo ""

echo "=== Large Responses (> 5KB) ==="
cat /tmp/access.log | awk '$10 > 5000 {print $10, $7}' | sort -rn | head -10

出力:

TEXT
=== Response Size Analysis (using $10 as simulated response size) ===
Largest response: 10098 bytes
Average response: 5142 bytes

=== Large Responses (> 5KB) ===
10098 /images/logo.png
 9876 /api/users
 8765 /api/orders
 7654 /products
 6543 /blog
 5678 /api/data

(8) ▶ 総合例:完全なログ分析レポートスクリプト

BASH
#!/bin/bash
# log-report.sh - Generate Nginx log analysis report

LOG_FILE="${1:-/tmp/access.log}"

if [ ! -f "$LOG_FILE" ]; then
    echo "Error: Log file $LOG_FILE does not exist"
    exit 1
fi

TOTAL=$(wc -l < "$LOG_FILE")

echo "========================================="
echo "     Nginx Access Log Analysis Report"
echo "     File: $LOG_FILE"
echo "     Total requests: $TOTAL"
echo "     Generated at: $(date)"
echo "========================================="
echo ""

# 1. Status code overview
echo "1. HTTP Status Code Distribution"
echo "--------------------"
cat "$LOG_FILE" | awk '{print $9}' | sort | uniq -c | sort -rn | \
    awk '{printf "  %-5s %s\n", $2, $1}'
echo ""

# 2. TOP 10 IPs
echo "2. Top 10 IPs by Access Count"
echo "---------------------"
cat "$LOG_FILE" | awk '{print $1}' | sort | uniq -c | sort -rn | head -10 | \
    awk '{printf "  %-20s %s\n", $2, $1}'
echo ""

# 3. TOP 10 URLs
echo "3. Top 10 URLs by Request Count"
echo "-----------------------"
cat "$LOG_FILE" | awk '{print $7}' | sort | uniq -c | sort -rn | head -10 | \
    awk '{printf "  %-30s %s\n", $1, $2}'
echo ""

# 4. 4xx/5xx errors
echo "4. Error Requests"
echo "-------------"
FOURXX=$(awk '$9 ~ /^4/ {count++} END {print count}' "$LOG_FILE")
FIVEXX=$(awk '$9 ~ /^5/ {count++} END {print count}' "$LOG_FILE")
echo "  4xx errors: $FOURXX ($(echo "scale=1; $FOURXX*100/$TOTAL" | bc)%)"
echo "  5xx errors: $FIVEXX ($(echo "scale=1; $FIVEXX*100/$TOTAL" | bc)%)"
echo ""

# 5. Requests per hour
echo "5. Requests per Hour"
echo "--------------------"
cat "$LOG_FILE" | awk -F: '{print $2":00"}' | sort | uniq -c | sort -rn | head -10 | \
    awk '{printf "  %s: %s\n", $2, $1}'
echo ""

# 6. Largest responses
echo "6. Largest Responses (Top 5)"
echo "----------------------"
cat "$LOG_FILE" | awk '{print $10, $7}' | sort -rn | head -5 | \
    awk '{printf "  %s bytes: %s\n", $1, $2}'
echo ""

echo "========================================="
echo "End of Report"
BASH
# Run the report script
chmod +x log-report.sh
./log-report.sh /tmp/access.log

❓ よくある質問

Q: Nginxログフォーマットの各フィールドの意味は? A: 標準combinedフォーマット:IP - ユーザー [時刻] "メソッド URL プロトコル" ステータスコード バイト "Referer" "User-Agent"。Nginxのlog_formatディレクティブでカスタマイズ可能。

Q: 巨大(GB級)ログファイルのパイプライン処理で詰まりますか? A: それほどひどくはありません。パイプラインストリーミングにより、データは読み込まれながら処理されます。ただしsortは全データを先読みする必要があるためブロックします。非常に大きいファイルの場合はsort -S 1Gでメモリを制限するか、awk配列で統計を行ってください。

Q: 本物のPV(ページビュー)とUV(ユニークビジター)のカウント方法は? A: PV = wc -l(画像/CSS/APIなど非ページリクエストを除く)。UV = awk '{print $1}' access.log | sort -u | wc -l

Q: ログの変化を継続的に監視するには? A: tail -fで新しい行をリアルタイム追跡。grepと組み合わせ:tail -f access.log | grep " 500 " — 500エラーをリアルタイム表示。

Q: APIエンドポイントのP99レスポンスタイムの計算方法は? A: Nginxで$upstream_response_timeまたは$request_timeを設定する必要があります。その後:awk '{print $NF}' access.log | sort -n | awk '{all[NR]=$1} END{p99=int(NR*0.99); print all[p99]}'


📖 まとめ


📝 練習問題

  1. 基本(難易度 ⭐):サンプルログ生成スクリプトを実行して1000件のテストデータを作成し、パイプラインチェーンでアクセス数トップ5のIPとそのリクエスト数を見つける
  2. 中級(難易度 ⭐⭐):404と500ステータスコードの数と割合をカウントし、awk '{print $7}'でURLを抽出して最も人気のあるページトップ5を見つける
  3. 上級(難易度 ⭐⭐⭐):フォーマット済みMarkdownレポートを出力するログ分析スクリプトを作成 — 総リクエスト数、Top 10 IP、ステータスコード分布、時間別リクエストトレンド、Top 5アクセスURLを含む
Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%