awk ile Sistem Log Raporu Oluşturma

Sistem yöneticilerinin günlük hayatında log dosyaları okumak kadar sıkıcı, ama aynı zamanda o kadar kritik bir görev yoktur. Yüzlerce megabaytlık log dosyasının içinden anlamlı bilgi çıkarmak, sorunları tespit etmek ve düzenli raporlar üretmek; işte tam bu noktada awk adeta bir süper güce dönüşüyor. Bu yazıda awk‘ı kullanarak gerçek dünya senaryolarında sistem log raporları nasıl oluşturulur, adım adım inceleyeceğiz.

awk Neden Log Analizi İçin Bu Kadar Güçlü?

awk temelde bir metin işleme dilidir. Ama onu diğer araçlardan ayıran şey, satır bazlı işlem yaparken aynı zamanda alanları (field) otomatik olarak parçalaması ve bunlar üzerinde programatik işlem yapabilmesidir. Log dosyaları yapısal olarak genellikle boşluk ya da belirli bir delimiter ile ayrılmış alanlardan oluşur. Bu da awk‘ı log analizi için biçilmiş kaftan yapar.

Temel çalışma mantığını hatırlayalım:

awk 'PATTERN { ACTION }' dosya.log

awk her satırı okur, pattern’e uyup uymadığını kontrol eder, uyuyorsa action bloğunu çalıştırır. Bu basit mantık üzerine kurulu onlarca özellik sayesinde tek satır komutlardan tam teşekküllü raporlama scriptlerine kadar her şeyi yazabilirsiniz.

Önemli birkaç dahili değişkeni hatırlayalım:

  • $0: Tüm satır
  • $1, $2, …$NF: Sırasıyla alanlar (1. alan, 2. alan, son alan)
  • NF: Satırdaki alan sayısı
  • NR: O ana kadar işlenen toplam satır sayısı
  • FS: Alan ayırıcı (Field Separator), varsayılan boşluk
  • OFS: Çıktı alan ayırıcısı
  • BEGIN: Dosya işlenmeden önce çalışır
  • END: Dosya işlendikten sonra çalışır

Nginx/Apache Access Log Analizi

Web sunucusu log analizinden başlayalım, çünkü bu en sık karşılaşılan senaryolardan biri. Tipik bir Nginx access log satırı şöyle görünür:

192.168.1.105 - - [15/Jan/2024:10:23:45 +0300] "GET /api/users HTTP/1.1" 200 1523

En Çok İstek Gönderen IP’leri Bulma

awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20

Bu komut tek başına bile çok işe yarar ama awk‘ı daha akıllıca kullanalım. Hem IP’yi hem de kaç istek gönderdiğini ve toplam transfer boyutunu tek seferde raporlayalım:

awk '
{
    ip_count[$1]++
    ip_bytes[$1] += $NF
}
END {
    print "IP AdresittIstek SayisitToplam Bytes"
    print "--------------------------------------------"
    for (ip in ip_count) {
        printf "%-20st%dtt%dn", ip, ip_count[ip], ip_bytes[ip]
    }
}
' /var/log/nginx/access.log | sort -t$'t' -k2 -rn | head -20

HTTP Durum Kodlarına Göre Özet Rapor

Hangi HTTP durum kodunun kaç kez döndüğünü görmek, sunucunuzun genel sağlığı hakkında çok hızlı bilgi verir:

awk '
{
    # HTTP durum kodu 9. alanda
    status[$9]++
    total++
}
END {
    print "n=== HTTP Durum Kodu Raporu ==="
    print "Toplam Istek: " total
    print "------------------------------"
    for (code in status) {
        pct = (status[code] / total) * 100
        printf "HTTP %s: %d istek (%.1f%%)n", code, status[code], pct
    }
}
' /var/log/nginx/access.log

Bu script çalıştığında şöyle bir çıktı alırsınız:

=== HTTP Durum Kodu Raporu ===
Toplam Istek: 45823
------------------------------
HTTP 200: 38291 istek (83.6%)
HTTP 404: 4521 istek (9.9%)
HTTP 500: 127 istek (0.3%)
HTTP 301: 2884 istek (6.2%)

404 oranı yüksekse kırık linklerin peşine düşmeniz, 500 oranı artıyorsa uygulama tarafında ciddi bir sorun olduğunu anlamanız gerekiyor demektir.

Saatlik Trafik Analizi

Zirve saatlerinizi bilmek, kapasite planlaması için altın değerinde bilgidir:

awk '
{
    # Tarih/saat alanından saati cek: [15/Jan/2024:10:23:45
    match($4, /[0-9]{2}:[0-9]{2}:[0-9]{2}/)
    saat = substr($4, RSTART, 2)
    hourly[saat]++
}
END {
    print "n=== Saatlik Trafik Dagilimi ==="
    for (h = 0; h <= 23; h++) {
        saat = sprintf("%02d", h)
        sayi = hourly[saat] + 0
        bar = ""
        scaled = int(sayi / 50)
        for (i = 0; i < scaled; i++) bar = bar "#"
        printf "%s:00 | %-40s %dn", saat, bar, sayi
    }
}
' /var/log/nginx/access.log

Bu komut size terminal üzerinde basit bir bar grafik bile çizer. Gece 03:00’te trafik zirvesi varsa ve bir saldırı şüpheniz yoksa, belki bir cron job’ın zamanlamasını gözden geçirmeniz gerekiyordur.

Sistem Auth Log Analizi – Brute Force Tespiti

/var/log/auth.log veya /var/log/secure dosyası, SSH brute force saldırılarını tespit etmek için en değerli kaynaktır:

awk '
/Failed password/ {
    # "from 192.168.1.1 port" formatindan IP cekme
    for (i=1; i<=NF; i++) {
        if ($i == "from") {
            ip = $(i+1)
            failed[ip]++
        }
    }
}
/Accepted password/ {
    for (i=1; i<=NF; i++) {
        if ($i == "from") {
            ip = $(i+1)
            success[ip]++
        }
    }
}
END {
    print "=== SSH Baglanti Raporu ==="
    print "n--- Basarisiz Giris Denemeleri (Potansiyel Brute Force) ---"
    for (ip in failed) {
        if (failed[ip] >= 10) {
            printf "UYARI: %-20s -> %d basarisiz denemen", ip, failed[ip]
        }
    }
    print "n--- Basarili Girisler ---"
    for (ip in success) {
        printf "%-20s -> %d basarili girisn", ip, success[ip]
    }
}
' /var/log/auth.log

Buradaki failed[ip] >= 10 eşiğini ortamınıza göre ayarlayabilirsiniz. Bazı yöneticiler 5’e çekiyor, bazıları 20’de bırakıyor. Kritik sistemlerde 3 bile fazla olabilir.

Syslog Üzerinden Servis Hata Analizi

/var/log/syslog veya /var/log/messages dosyası onlarca servisin logunu bir arada tutar. İçinden sadece hataları ve hangi servisten geldiğini raporlayalım:

awk '
/error|Error|ERROR|fail|Fail|FAIL|critical|Critical|CRITICAL/ {
    # Syslog formati: Jan 15 10:23:45 hostname servisadi[pid]: mesaj
    servis = $5
    gsub(/[.*]/, "", servis)  # PID kismi kaldir
    gsub(/:$/, "", servis)       # Sondaki iki noktayi kaldir
    
    errors[servis]++
    
    if (errors[servis] <= 3) {
        ornek[servis] = ornek[servis] "n    " $0
    }
}
END {
    print "=== Servis Bazli Hata Ozeti ==="
    print "Toplam hata iceren servis: " length(errors)
    print ""
    
    for (svc in errors) {
        printf "[ %s ] -> %d hata/uyarin", svc, errors[svc]
        if (ornek[svc] != "") {
            print "  Ornekler:" ornek[svc]
        }
        print ""
    }
}
' /var/log/syslog

Bu script size hangi servisin ne kadar gürültü çıkardığını anında gösterir. Bazen bir servis tek başına binlerce hata üretiyor ve diğer kritik hataların gözden kaçmasına neden oluyordur.

Disk I/O ve Kernel Log Analizi

Kernel panic, OOM (Out of Memory) killer devreye girme gibi ciddi sistem olaylarını tespit etmek için:

awk '
/Out of memory/ || /oom_kill/ {
    oom_count++
    oom_log[oom_count] = $0
}
/Kernel panic/ {
    panic_count++
    panic_log[panic_count] = $0
}
/EXT4-fs error/ || /XFS.*error/ || /I/O error/ {
    disk_errors++
    if (disk_errors <= 5) {
        disk_log[disk_errors] = $0
    }
}
/segfault/ {
    segv[$NF]++
}
END {
    print "=========================================="
    print "KRITIK SISTEM OLAY RAPORU"
    print "=========================================="
    
    printf "nOOM (Bellek Yetersizligi) Olaylari: %dn", oom_count+0
    for (i=1; i<=oom_count && i<=3; i++) {
        print "  >> " oom_log[i]
    }
    
    printf "nKernel Panic Sayisi: %dn", panic_count+0
    for (i=1; i<=panic_count; i++) {
        print "  >> " panic_log[i]
    }
    
    printf "nDisk I/O Hatalari: %dn", disk_errors+0
    for (i=1; i<=disk_errors && i<=5; i++) {
        print "  >> " disk_log[i]
    }
    
    if (length(segv) > 0) {
        print "nSegfault Olaylari:"
        for (prog in segv) {
            printf "  %s: %d kezn", prog, segv[prog]
        }
    }
}
' /var/log/kern.log

OOM killer devreye girmişse sisteminizde ciddi bir bellek baskısı var demektir. Bu raporu her sabah çalıştırılacak bir cron job haline getirirseniz, gece olan olayları kahvenizi içerken öğrenmiş olursunuz.

Günlük Otomatik Log Rapor Scripti

Tüm bu analizleri bir araya getirerek her gün e-posta ile gönderilecek kapsamlı bir rapor scripti yazalım:

#!/bin/bash
# /usr/local/bin/daily-log-report.sh

RAPOR_DOSYASI="/tmp/gunluk-log-raporu-$(date +%Y%m%d).txt"
EPOSTA="[email protected]"
NGINX_LOG="/var/log/nginx/access.log"
AUTH_LOG="/var/log/auth.log"
SYSLOG="/var/log/syslog"

echo "GUNLUK SISTEM LOG RAPORU - $(date '+%d/%m/%Y %H:%M')" > $RAPOR_DOSYASI
echo "================================================" >> $RAPOR_DOSYASI

# 1. Web Sunucu Ozeti
if [ -f "$NGINX_LOG" ]; then
    echo -e "n## WEB SUNUCU OZETI (Son 24 Saat)" >> $RAPOR_DOSYASI
    awk -v tarih="$(date -d 'yesterday' '+%d/%b/%Y')" '
    $0 ~ tarih {
        toplam++
        durum[$9]++
        if ($9 >= 500) hata500++
        if ($9 == 404) hata404++
    }
    END {
        printf "Toplam Istek: %dn", toplam
        printf "404 Hatalari: %dn", hata404+0
        printf "500 Hatalari: %dn", hata500+0
    }
    ' $NGINX_LOG >> $RAPOR_DOSYASI
fi

# 2. SSH Guvenlik Ozeti
if [ -f "$AUTH_LOG" ]; then
    echo -e "n## SSH GUVENLIK OZETI" >> $RAPOR_DOSYASI
    awk '
    /Failed password/ {
        for(i=1;i<=NF;i++) if($i=="from") failed[$(i+1)]++
    }
    END {
        toplam = 0
        en_fazla_ip = ""
        en_fazla_sayi = 0
        for(ip in failed) {
            toplam += failed[ip]
            if(failed[ip] > en_fazla_sayi) {
                en_fazla_sayi = failed[ip]
                en_fazla_ip = ip
            }
        }
        printf "Toplam basarisiz SSH denemesi: %dn", toplam
        printf "En agresif IP: %s (%d deneme)n", en_fazla_ip, en_fazla_sayi
        printf "Uniq saldirgan IP sayisi: %dn", length(failed)
    }
    ' $AUTH_LOG >> $RAPOR_DOSYASI
fi

# 3. Sistem Hata Ozeti
echo -e "n## SISTEM HATA OZETI" >> $RAPOR_DOSYASI
awk '
/error|Error|ERROR/ { errors++ }
/warning|Warning|WARNING/ { warnings++ }
/critical|Critical|CRITICAL/ { criticals++ }
END {
    printf "Kritik: %dn", criticals+0
    printf "Hata: %dn", errors+0
    printf "Uyari: %dn", warnings+0
}
' $SYSLOG >> $RAPOR_DOSYASI

echo -e "n================================================" >> $RAPOR_DOSYASI
echo "Rapor olusturuldu: $(date)" >> $RAPOR_DOSYASI

# E-posta gonder
mail -s "Gunluk Log Raporu - $(hostname) - $(date '+%d/%m/%Y')" $EPOSTA < $RAPOR_DOSYASI

echo "Rapor olusturuldu ve gonderildi: $RAPOR_DOSYASI"

Bu scripti cron’a eklemek için:

chmod +x /usr/local/bin/daily-log-report.sh
echo "0 7 * * * root /usr/local/bin/daily-log-report.sh" >> /etc/crontab

Her sabah 07:00’de masanıza bir rapor düşmüş olacak.

awk ile Log Rotasyonu Sonrası Analiz

Log dosyaları rotate edildiğinde birden fazla dosyayı analiz etmeniz gerekebilir. awk‘ın birden fazla dosyayla çalışma yeteneğini kullanalım:

awk '
FNR == 1 { 
    print "n--- Dosya: " FILENAME " ---"
    dosya_sayac++
}
/error|ERROR/ {
    errors[FILENAME]++
}
END {
    print "n=== Dosya Bazli Hata Ozeti ==="
    for (dosya in errors) {
        printf "%s: %d hatan", dosya, errors[dosya]
    }
    print "Toplam analiz edilen dosya: " dosya_sayac
}
' /var/log/nginx/access.log /var/log/nginx/access.log.1 /var/log/nginx/access.log.2

Ya da glob ile tüm rotate dosyaları otomatik dahil edin:

awk '/error/{errors[FILENAME]++} END{for(f in errors) printf "%s: %dn",f,errors[f]}' 
    /var/log/nginx/access.log*

Performans İpuçları ve Dikkat Edilmesi Gerekenler

Büyük log dosyalarında awk kullanırken bazı pratik noktalara dikkat etmek gerekiyor:

  • -F kullanımı: Eğer log dosyanız virgül ya da iki nokta ile ayrılmışsa awk -F':' '{print $1}' şeklinde field separator belirtin. Varsayılan boşluk ayırıcısı bazen birden fazla boşluğu tek sayar, bu bazı logları yanlış parse etmenize neden olabilir.
  • Büyük dosyalarda bellek yönetimi: awk‘ta dizilere (arrays) veri biriktirirken dikkatli olun. Milyonlarca unique IP veya URL varsa, tüm bunları bellekte tutmak sisteminizi yorabilir. Bu durumda sort | uniq -c kombinasyonuyla önceden filtreleyin.
  • gawk vs mawk: Bazı sistemlerde gawk (GNU awk) yerine mawk yüklüdür. mawk daha hızlıdır ama bazı gelişmiş gawk özelliklerini desteklemez. match() fonksiyonunun gelişmiş kullanımı gibi şeyler için gawk olduğundan emin olun.
  • Büyük/küçük harf duyarsız arama: /error/I şeklinde I flag’i kullanarak büyük/küçük harf duyarsız arama yapabilirsiniz. Bu gawk‘a özgü bir özellik.
  • OFMT ve CONVFMT: Ondalıklı sayı hesaplamalarında çıktı formatını kontrol etmek için awk 'BEGIN{OFMT="%.2f"}' kullanabilirsiniz. Log raporlarında yüzde hesaplarken işe yarar.
  • Pipe ile kullanım: awk‘ı grep ile kombine etmek bazen daha hızlı çalışır. Önce grep ile satır sayısını düşürün, sonra awk ile işleyin: grep "ERROR" app.log | awk '{print $5}' | sort | uniq -c.
  • stdin okuma: awk bir dosya belirtmezseniz stdin’den okur. Bu sayede pipe chain’lerinde rahatça kullanabilirsiniz.

Gerçek Dünya Senaryosu: Uygulama Yavaşlama Tespiti

Bir uygulama log dosyasında response time loglanıyorsa, yavaş istekleri tespit etmek çok değerli olabilir:

awk '
# Ornek log: 2024-01-15 10:23:45 GET /api/data 200 1523ms
{
    response_time = $NF
    gsub(/ms/, "", response_time)
    response_time = response_time + 0
    
    toplam += response_time
    sayac++
    
    if (response_time > maks) {
        maks = response_time
        maks_satir = $0
    }
    
    if (response_time >= 1000) yavas++
    if (response_time >= 5000) cok_yavas++
    
    endpoint = $4
    endpoint_sure[endpoint] += response_time
    endpoint_sayi[endpoint]++
}
END {
    ortalama = toplam / sayac
    printf "n=== Response Time Analizi ===n"
    printf "Toplam istek: %dn", sayac
    printf "Ortalama: %.1f msn", ortalama
    printf "Maksimum: %d msn", maks
    printf "1 saniye ustu: %d (%.1f%%)n", yavas, (yavas/sayac)*100
    printf "5 saniye ustu: %d (%.1f%%)n", cok_yavas, (cok_yavas/sayac)*100
    printf "nEn Yavash Endpoint:n  %sn", maks_satir
    
    print "n=== Endpoint Bazli Ortalama Sure ==="
    for (ep in endpoint_sure) {
        avg = endpoint_sure[ep] / endpoint_sayi[ep]
        if (avg > 500) {
            printf "%-40s %.1f ms (%d istek)n", ep, avg, endpoint_sayi[ep]
        }
    }
}
' /var/log/myapp/access.log

Bu analiz sayesinde hangi endpoint’in yavaşladığını, genel response time dağılımını ve acil dikkat gerektiren alanları tek komutla öğrenebilirsiniz.

Sonuç

awk log analizi söz konusu olduğunda gerçekten eşsiz bir araç. Öğrenme eğrisi biraz dik görünebilir ama bir kez içselleştirdiğinizde, log dosyalarına bakış açınız tamamen değişiyor. Artık “bu dosyada ne var acaba?” diye sormak yerine, tam olarak neye baktığınızı bilen, verileri istediğiniz şekilde şekillendiren biri oluyorsunuz.

Yazdığımız scriptleri kendi ortamınıza uyarlarken birkaç şeyi aklınızda bulundurun: Her ortamın log formatı biraz farklıdır, field numaraları değişebilir. Scripti üretim sistemine almadan önce birkaç satırla test edin. awk 'NR<=5{print}' dosya.log ile ilk 5 satırı görüp field yapısını anlayın, sonra script yazın.

Günlük otomatik rapor scriptini mutlaka kurun. Sabah kahvenizi içerken sistemin gece neler yaptığını, kaç brute force denemesi geldiğini, hangi servisten kaç hata geldiğini bir e-posta ile öğrenmek; olası sorunlara reaktif değil proaktif yaklaşmanızı sağlıyor. Bu fark, ciddi bir olayda saatlerce geçen bir kesinti ile birkaç dakikada tespit edilen bir anomali arasındaki fark olabilir.

Yorum yapın