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 durumdasort | uniq -ckombinasyonuyla önceden filtreleyin.
- gawk vs mawk: Bazı sistemlerde
gawk(GNU awk) yerinemawkyüklüdür.mawkdaha hızlıdır ama bazı gelişmişgawközelliklerini desteklemez.match()fonksiyonunun gelişmiş kullanımı gibi şeyler içingawkolduğundan emin olun.
- Büyük/küçük harf duyarsız arama:
/error/IşeklindeIflag’i kullanarak büyük/küçük harf duyarsız arama yapabilirsiniz. Bugawk‘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‘ıgrepile kombine etmek bazen daha hızlı çalışır. Öncegrepile satır sayısını düşürün, sonraawkile işleyin:grep "ERROR" app.log | awk '{print $5}' | sort | uniq -c.
- stdin okuma:
awkbir 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.