Linux: مشروع المرحلة 3 المصغّر — تحليل سجلات الخادم
هذا المشروع الختامي للمرحلة 3. ستدمج إعادة التوجيه والأنابيب وgrep وawk وsed وsort وuniq وكل مهارات معالجة البيانات لإجراء تحليل كامل الأبعاد لسجلات وصول Nginx وتوليد تقرير.
📋 المتطلبات المسبقة: يجب إكمال
- الدرس 12: التدفقات القياسية وإعادة التوجيه
- الدرس 13: الأنابيب والمرشحات
- الدرس 14: البحث في النص
- الدرس 15: الفرسان الثلاثة لمعالجة النصوص
1. ما ستتعلمه
- تحليل صياغة سجلات Nginx
- تحليل حجم الطلبات حسب الفترة الزمنية
- ترتيب عناوين URL الأكثر زيارة
- إحصاءات توزيع رموز الحالة
- تحليل أداء أوقات الاستجابة
2. موقع شياو مينغ أصبح بطيئًا
(1) نقطة الألم: المدير يريد بيانات، لكن سجل الـ10 غيغابايت يستحيل تصفحه
موقع شياو مينغ أصبح بطيئًا مؤخرًا، والمدير طلب "أعطني بيانات." سجل access.log الخاص بـNginx يولّد مئات الميغابايت يوميًا — فتحه مستحيل، والبحث بكلمات مفتاحية كالبحث عن إبرة في كومة قش.
(2) تحليل بسلسلة أنابيب
استخدم شياو مينغ مهارات المرحلة 3، مستخرجًا المقاييس الأساسية سطرًا بسطر بأوامر الأنابيب:
# أكثر 10 عناوين IP تكرارًا
cat access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -10
# أكثر 10 عناوين URL طلبًا
cat access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -10
> 💡 نصيحة: نمط أنبوب تحليل السجلات ثابت: `awk استخراج الحقول → sort → uniq -c إزالة التكرار والعدّ → sort -rn تنازلي حسب العدد → head أخذ أعلى N`. تذكّر هذا القالب — أي تحليل Top N للسجلات يمكنه استخدامه.
# الطلبات في كل ساعة
cat access.log | awk -F: '{print $2":00"}' | sort | uniq -c | sort -rn
الإخراج:
TEXT210 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)
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
ℹ️ ملاحظة: في صياغة combined لـNginx، الحقول مفصولة بمسافات، لكن بعض الحقول تحتوي مسافات بداخلها (مثل سطر الطلب والطابع الزمني ملفوفان بأقواس وعلامات اقتباس). عندما يقسم awk بمسافات،
$1هو IP، و$7هو URL، و$9هو رمز الحالة، و$10هو البايتات — مواضع هذه الحقول ثابتة ويمكن استخراجها مباشرة بـawk.
مواضع الحقول (مفصولة بمسافات):
| الحقل | المعنى |
|---|---|
$1 |
IP العميل |
$4 |
جزء [ من الطابع الزمني |
$7 |
URL المطلوب |
$9 |
رمز حالة HTTP |
$10 |
بايتات الاستجابة |
(2) ▶ مثال: توليد سجلات عينة
أولًا، أنشئ ملف سجل عينة للتحليل:
#!/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"
chmod +x generate-sample-log.sh
./generate-sample-log.sh
الإخراج:
TEXTGenerated 1000 sample log entries to /tmp/access.log
(3) ▶ مثال: إحصاءات حجم الطلبات
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) ▶ مثال: تحليل رموز الحالة
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
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) ▶ مثال: تحليل البُعد الزمني
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) ▶ مثال: تحليل الأداء
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) ▶ مثال شامل: سكريبت تقرير تحليل السجلات الكامل
#!/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. نظرة عامة على رموز الحالة
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. أعلى 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. أعلى 10 عناوين URL
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
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. الطلبات في كل ساعة
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. أكبر الاستجابات
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"
# تشغيل سكريبت التقرير
chmod +x log-report.sh
./log-report.sh /tmp/access.log
❓ أسئلة شائعة
س: ماذا تعني الحقول في صياغة سجل Nginx؟ ج: الصياغة القياسية combined: IP - مستخدم [وقت] "طريقة URL بروتوكول" رمز_الحالة بايت "Referer" "User-Agent". يمكنك تخصيصها عبر توجيه
log_formatالخاص بـNginx.
س: هل ستختنق الأنابيب مع ملفات سجلات ضخمة (بمستوى GB)؟ ج: ليس كثيرًا. التدفق الانسيابي للأنابيب يعني أن البيانات تُعالج أثناء قراءتها. لكن
sortسيحجب لأنه يحتاج قراءة كل البيانات أولًا. للملفات الكبيرة جدًا، استخدمsort -S 1Gلتحديد الذاكرة، أو استخدم مصفوفات awk للإحصاءات بدلًا من ذلك.
س: كيف أعُدّ PV (مشاهدات الصفحة) وUV (الزوار الفريدين) الحقيقيين؟ ج: PV =
wc -l(باستثناء الطلبات غير الصفحية مثل الصور/CSS/API). UV =awk '{print $1}' access.log | sort -u | wc -l.
س: كيف أراقب تغييرات السجل باستمرار؟ ج:
tail -fيتتبع الأسطر الجديدة في الوقت الفعلي. ادمج مع grep:tail -f access.log | grep " 500 "— يعرض كل أخطاء 500 في الوقت الفعلي.
س: كيف أحسب وقت الاستجابة P99 لنقاط نهاية API؟ ج: يحتاج 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]}'.
📖 ملخص
- صياغة سجل Nginx القياسية: IP/وقت/URL/رمز حالة/حجم
awk '{print $1}'يستخرج IP، و$7يستخرج URL، و$9يستخرج رمز الحالةsort | uniq -c | sort -rn | headلإيجاد Top Nawk -F: '{print $2":00"}'للتجميع حسب الساعة- سكريبت تحليل سجلات كامل يحتاج فقط 30-50 سطرًا من bash
📝 تمارين
- أساسي (⭐): شغّل سكريبت توليد السجلات العينة لإنشاء 1000 إدخال اختبار، ثم اكتب سلسلة أنابيب لإيجاد أكثر 5 عناوين IP تكرارًا وعدد طلباتها
- متوسط (⭐⭐): عُدّ عدد ونسبة رموز الحالة 404 و500، ثم استخدم
awk '{print $7}'لاستخراج عناوين URL وإيجاد أكثر 5 صفحات شعبية - متقدم (⭐⭐⭐): أنشئ سكريبت تحليل سجلات يُخرج تقريرًا منسّقًا بـMarkdown يتضمن: إجمالي الطلبات، وأعلى 10 IPs، وتوزيع رموز الحالة، واتجاهات الطلبات بالساعة، وأكثر 5 عناوين URL وصولًا