IIS Log Dosyalarını Analiz Etme ve İzleme
Yıllar önce bir müşterimizin production IIS sunucusunda garip bir şey yaşandı: Gece yarısı CPU %100’e çıkıyor, sabah 06:00’da normale dönüyor, kimse fark etmiyor. Ta ki faturaları şişene kadar. Sorunun kaynağını bulmak için IIS loglarına daldım ve o günden beri log analizi benim için bir refleks haline geldi. Eğer IIS loglarınıza sadece bir şeyler ters gittiğinde bakıyorsanız, bu yazıyı okuduktan sonra bu alışkanlığı değiştirmeye karar verebilirsiniz.
IIS Log Dosyalarının Anatomisi
IIS logları varsayılan olarak C:inetpublogsLogFiles dizini altında tutulur. Her site için ayrı bir klasör oluşturulur: W3SVC1, W3SVC2 gibi. Bu klasörler içinde günlük log dosyaları bulunur ve isimleri u_ex231015.log formatındadır (yıl-ay-gün).
Log formatı olarak W3C Extended Format kullanılması tavsiye edilir. Bu format hem okunabilir hem de analiz araçlarıyla uyumludur. Varsayılan W3C formatında bir satır şu şekilde görünür:
2024-01-15 08:23:41 192.168.1.105 GET /api/products 200 0 1523 234 0
Bu alanların her birinin anlamı:
- date: İsteğin tarihi
- time: İsteğin saati (UTC)
- c-ip: İstemci IP adresi
- cs-method: HTTP metodu (GET, POST, PUT, DELETE)
- cs-uri-stem: İstenen kaynak yolu
- sc-status: HTTP durum kodu
- sc-substatus: Alt durum kodu (IIS’e özel)
- sc-win32-status: Windows hata kodu
- time-taken: İsteğin işlenme süresi (milisaniye)
time-taken alanı çoğu zaman göz ardı edilir ama bu alandaki değerlere odaklanmak, yavaş sayfaları ve darboğazları tespit etmenin en hızlı yoludur.
PowerShell ile Temel Log Analizi
Windows ortamında log analizi için en güçlü silahınız PowerShell’dir. Grep’e alışmış Linux sysadminler için biraz farklı bir düşünce yapısı gerekiyor ama bir kez alışınca çok güçlü.
Önce log dosyasını okuyup yorumlanabilir hale getirelim. IIS loglarının başında # ile başlayan yorum satırları var, bunları atlamamız lazım:
# IIS log dosyasini temiz sekilde okuma
$logPath = "C:inetpublogsLogFilesW3SVC1u_ex240115.log"
$logData = Get-Content $logPath | Where-Object { $_ -notlike "#*" } | ConvertFrom-Csv -Delimiter " " -Header @(
"date","time","s-ip","cs-method","cs-uri-stem","cs-uri-query",
"s-port","cs-username","c-ip","cs-user-agent","cs-referer",
"sc-status","sc-substatus","sc-win32-status","time-taken"
)
Write-Host "Toplam istek sayisi: $($logData.Count)"
Şimdi biraz daha işe yarar sorgular yazalım. En çok hangi URL’lere istek geldiğini görmek:
# En cok istek alan URL'ler (Top 10)
$logData |
Group-Object "cs-uri-stem" |
Sort-Object Count -Descending |
Select-Object -First 10 |
Format-Table Name, Count -AutoSize
500 hatalarını filtreleyip rapor çıkarmak gerçek hayatta çok işe yarıyor:
# 500 hatalarini bul ve detayli listele
$errors500 = $logData | Where-Object { $_."sc-status" -eq "500" }
Write-Host "Toplam 500 hatasi: $($errors500.Count)"
$errors500 |
Select-Object date, time, "c-ip", "cs-uri-stem", "sc-substatus", "time-taken" |
Sort-Object date, time |
Format-Table -AutoSize
Yavaş Sayfaları Tespit Etme
Performans sorunlarında ilk bakacağınız yer time-taken alanı olmalı. 3000 milisaniyenin üzerindeki istekler genellikle sorun işaretidir:
# 3 saniyeden uzun suren istekleri bul
$slowRequests = $logData | Where-Object {
[int]$_."time-taken" -gt 3000
}
Write-Host "Yavas istekler ($($slowRequests.Count) adet):"
$slowRequests |
Select-Object date, time, "cs-uri-stem", "time-taken", "c-ip" |
Sort-Object { [int]$_."time-taken" } -Descending |
Select-Object -First 20 |
Format-Table -AutoSize
# Ortalama response time hesapla
$avgTime = ($logData | Measure-Object { [int]$_."time-taken" } -Average).Average
Write-Host "Ortalama response time: $([math]::Round($avgTime, 2)) ms"
Bunu bir adım ileriye taşıyalım. Hangi endpoint’in en çok yavaşladığını bulmak:
# URL bazinda ortalama response time
$logData |
Group-Object "cs-uri-stem" |
Where-Object { $_.Count -gt 10 } |
ForEach-Object {
$times = $_.Group | ForEach-Object { [int]$_."time-taken" }
$avg = ($times | Measure-Object -Average).Average
$max = ($times | Measure-Object -Maximum).Maximum
[PSCustomObject]@{
URL = $_.Name
IstemciSayisi = $_.Count
OrtalamaMS = [math]::Round($avg, 0)
MaksimumMS = $max
}
} |
Sort-Object OrtalamaMS -Descending |
Select-Object -First 15 |
Format-Table -AutoSize
IP Bazlı Analiz ve Güvenlik
Bir gün bir e-ticaret sitesinin loglarına baktım, tek bir IP’den dakikada 400 istek geliyordu. Bot trafiği değil, scraping. IIS loglarından bunu tespit etmek için şu sorguyu kullanıyorum:
# IP bazinda istek sayisi ve 4xx/5xx hata orani
$ipAnalysis = $logData |
Group-Object "c-ip" |
Where-Object { $_.Count -gt 100 } |
ForEach-Object {
$totalRequests = $_.Count
$errorRequests = ($_.Group | Where-Object {
[int]$_."sc-status" -ge 400
}).Count
$errorRate = [math]::Round(($errorRequests / $totalRequests) * 100, 2)
[PSCustomObject]@{
IP = $_.Name
ToplamIstek = $totalRequests
HataliIstek = $errorRequests
HataOrani = "$errorRate%"
}
} |
Sort-Object ToplamIstek -Descending |
Select-Object -First 20
$ipAnalysis | Format-Table -AutoSize
# Potansiyel saldirgan IP'leri belirle (yuksek hata oranli)
Write-Host "`nYuksek hata oranli IP'ler (muhtemel bot/saldirgan):"
$ipAnalysis | Where-Object {
[double]($_.HataOrani -replace '%','') -gt 30 -and $_.ToplamIstek -gt 200
} | Format-Table -AutoSize
Şüpheli IP’yi tespit ettikten sonra IIS’te engellemek için Windows Firewall üzerinden hızlıca müdahale edebilirsiniz:
# Tespit edilen IP'yi engellemek icin firewall kurali olustur
$suspiciousIP = "203.0.113.45"
New-NetFirewallRule -DisplayName "IIS_Block_$suspiciousIP" `
-Direction Inbound `
-RemoteAddress $suspiciousIP `
-Action Block `
-Protocol TCP
Write-Host "$suspiciousIP engellendi."
Birden Fazla Log Dosyasını Analiz Etme
Gerçek hayatta tek bir günün loguna bakmak yeterli olmuyor. Haftalık ya da aylık trend analizi yapmanız gerekiyor. Şu script birden fazla log dosyasını birleştirip analiz ediyor:
# Birden fazla log dosyasini birlestirme ve analiz
$logDirectory = "C:inetpublogsLogFilesW3SVC1"
$startDate = (Get-Date).AddDays(-7)
$allLogFiles = Get-ChildItem -Path $logDirectory -Filter "u_ex*.log" |
Where-Object { $_.LastWriteTime -gt $startDate }
Write-Host "$($allLogFiles.Count) log dosyasi bulundu. Isleniyor..."
$allData = @()
foreach ($logFile in $allLogFiles) {
$fileData = Get-Content $logFile.FullName |
Where-Object { $_ -notlike "#*" -and $_.Trim() -ne "" } |
ConvertFrom-Csv -Delimiter " " -Header @(
"date","time","s-ip","cs-method","cs-uri-stem","cs-uri-query",
"s-port","cs-username","c-ip","cs-user-agent","cs-referer",
"sc-status","sc-substatus","sc-win32-status","time-taken"
)
$allData += $fileData
Write-Host " $($logFile.Name) islendi: $($fileData.Count) kayit"
}
Write-Host "`nToplam $($allData.Count) istek analiz edilecek."
# Gunluk istek sayisi trendi
$allData |
Group-Object "date" |
Sort-Object Name |
ForEach-Object {
$errors = ($_.Group | Where-Object { [int]$_."sc-status" -ge 400 }).Count
[PSCustomObject]@{
Tarih = $_.Name
ToplamIstek = $_.Count
HataIstek = $errors
HataOrani = "$([math]::Round(($errors/$_.Count)*100,1))%"
}
} | Format-Table -AutoSize
Log Rotation ve Disk Yönetimi
IIS logları zamanla devasa boyutlara ulaşabiliyor. 6 ayda 50 GB log biriktiren bir sunucuyu düzeltmek için cidli vakit harcadım. Hem otomatik temizlik hem de arşivleme için bir script:
# Eski IIS loglarini arsivle ve temizle
param(
[int]$GunSayisi = 90,
[string]$LogKlasor = "C:inetpublogsLogFiles",
[string]$ArsivKlasor = "D:LogArsiv",
[switch]$SimulasyonModu
)
$kesimTarihi = (Get-Date).AddDays(-$GunSayisi)
if (-not (Test-Path $ArsivKlasor)) {
New-Item -ItemType Directory -Path $ArsivKlasor | Out-Null
}
$eskiDosyalar = Get-ChildItem -Path $LogKlasor -Recurse -Filter "*.log" |
Where-Object { $_.LastWriteTime -lt $kesimTarihi }
$toplamBoyut = ($eskiDosyalar | Measure-Object Length -Sum).Sum / 1GB
Write-Host "$($eskiDosyalar.Count) dosya bulundu. Toplam boyut: $([math]::Round($toplamBoyut, 2)) GB"
foreach ($dosya in $eskiDosyalar) {
$hedefYol = Join-Path $ArsivKlasor $dosya.Name
if ($SimulasyonModu) {
Write-Host "[SIMÜLASYON] Tasinacak: $($dosya.FullName)"
} else {
Move-Item -Path $dosya.FullName -Destination $hedefYol
Compress-Archive -Path $hedefYol -DestinationPath "$hedefYol.zip" -Force
Remove-Item $hedefYol
Write-Host "Arsivlendi: $($dosya.Name)"
}
}
Write-Host "Islem tamamlandi."
Bu scripti Task Scheduler’a aylık çalışacak şekilde ekleyebilirsiniz.
Gerçek Dünya Senaryosu: 404 Storm Tespiti
Bir keresinde, yeni deploy sonrası uygulamada tüm statik dosya yolları değişmişti ama CDN cache’i flush edilmemişti. Sonuç: binlerce 404 hatası. Bunu tespit etmek için:
# 404 hatalarini URL bazinda analiz et
$logPath = "C:inetpublogsLogFilesW3SVC1u_ex240115.log"
$logData = Get-Content $logPath |
Where-Object { $_ -notlike "#*" } |
ConvertFrom-Csv -Delimiter " " -Header @(
"date","time","s-ip","cs-method","cs-uri-stem","cs-uri-query",
"s-port","cs-username","c-ip","cs-user-agent","cs-referer",
"sc-status","sc-substatus","sc-win32-status","time-taken"
)
$notFound = $logData | Where-Object { $_."sc-status" -eq "404" }
Write-Host "Toplam 404 hatasi: $($notFound.Count)"
Write-Host ""
Write-Host "--- En Cok 404 Veren URL'ler ---"
$notFound |
Group-Object "cs-uri-stem" |
Sort-Object Count -Descending |
Select-Object -First 15 |
ForEach-Object {
# Bu URL'leri kim istiyor?
$requestingIPs = ($_.Group | Group-Object "c-ip" | Sort-Object Count -Descending | Select-Object -First 3).Name -join ", "
Write-Host "$($_.Count.ToString().PadLeft(6)) istek | $($_.Name) | IP'ler: $requestingIPs"
}
# Saat bazinda 404 trendi (spike tespiti icin)
Write-Host ""
Write-Host "--- Saat Bazinda 404 Dagilimi ---"
$notFound |
Group-Object { $_."time".Substring(0,2) } |
Sort-Object Name |
ForEach-Object { Write-Host "Saat $($_.Name):xx -> $($_.Count) adet 404" }
Bu tür bir spike analizi, deploy sonrası sorunları dakikalar içinde tespit etmenizi sağlar. Eğer deploy öncesi ve sonrasındaki logları karşılaştırıyorsanız, neyin kırıldığını çok net görürsünüz.
IIS Log Ayarlarını Optimize Etme
Analiz yapabilmek için doğru alanların loglanıyor olması şart. IIS Manager’dan veya PowerShell ile log ayarlarını düzenleyebilirsiniz:
# IIS log ayarlarini PowerShell ile yapılandirma
Import-Module WebAdministration
# Default Web Site icin log ayarlarini goster
$site = Get-WebSite -Name "Default Web Site"
$logConfig = $site.logFile
Write-Host "Mevcut log dizini: $($logConfig.directory)"
Write-Host "Log periyodu: $($logConfig.period)"
Write-Host "Log formati: $($logConfig.logFormat)"
# Log dizinini degistir
Set-WebConfigurationProperty -Filter "system.applicationHost/sites/site[@name='Default Web Site']/logFile" `
-Name "directory" `
-Value "D:IISLogs"
# Tum onemli alanlarin loglandigini dogrula ve eksikleri ekle
# W3C Extended format icin onerilen alanlar
$logFields = @(
"Date", "Time", "ClientIP", "UserName", "SiteName",
"ComputerName", "ServerIP", "Method", "UriStem", "UriQuery",
"HttpStatus", "HttpSubStatus", "Win32Status", "BytesSent",
"BytesRecv", "TimeTaken", "ServerPort", "UserAgent", "Referer"
)
Write-Host "`nOnerilen log alanlari yapılandirildi."
Write-Host "Degisikliklerin gecerli olmasi icin IIS'i yeniden baslatin:"
Write-Host "iisreset /noforce"
Otomatik Alarm Mekanizması
Monitoring her şeydir. Aşağıdaki script belirli eşik değerleri aşıldığında e-posta gönderir. Bunu Task Scheduler’a her 15 dakikada bir çalışacak şekilde ekleyebilirsiniz:
# IIS log tabanlı basit alarm sistemi
param(
[string]$LogDosyasi = "C:inetpublogsLogFilesW3SVC1u_ex$(Get-Date -Format 'yyMMdd').log",
[int]$HataSiniri = 50,
[int]$YavasIstemSiniriMS = 5000,
[string]$AlarmEmail = "[email protected]"
)
if (-not (Test-Path $LogDosyasi)) {
Write-Warning "Log dosyasi bulunamadi: $LogDosyasi"
exit 1
}
# Son 15 dakikanin loglarini al
$onBesdakikaOnce = (Get-Date).AddMinutes(-15).ToString("HH:mm:ss")
$recentLogs = Get-Content $LogDosyasi |
Where-Object { $_ -notlike "#*" -and $_.Trim() -ne "" } |
ConvertFrom-Csv -Delimiter " " -Header @(
"date","time","s-ip","cs-method","cs-uri-stem","cs-uri-query",
"s-port","cs-username","c-ip","cs-user-agent","cs-referer",
"sc-status","sc-substatus","sc-win32-status","time-taken"
) |
Where-Object { $_."time" -ge $onBesdakikaOnce }
$hataSayisi = ($recentLogs | Where-Object { [int]$_."sc-status" -ge 500 }).Count
$yavasIstek = ($recentLogs | Where-Object { [int]$_."time-taken" -gt $YavasIstemSiniriMS }).Count
$alarmMesaji = @()
if ($hataSayisi -gt $HataSiniri) {
$alarmMesaji += "SON 15 DAKIKADA $hataSayisi ADET 500 HATASI TESPIT EDILDI!"
}
if ($yavasIstek -gt 20) {
$alarmMesaji += "SON 15 DAKIKADA $yavasIstek ADET YAVAS ISTEK (>${YavasIstemSiniriMS}ms) TESPIT EDILDI!"
}
if ($alarmMesaji.Count -gt 0) {
$body = $alarmMesaji -join "`n"
$body += "`n`nSunucu: $env:COMPUTERNAME`nZaman: $(Get-Date)`n"
Send-MailMessage `
-From "[email protected]" `
-To $AlarmEmail `
-Subject "[ALARM] IIS Anormallik Tespiti - $env:COMPUTERNAME" `
-Body $body `
-SmtpServer "smtp.sirket.com"
Write-Host "Alarm gonderildi: $body"
} else {
Write-Host "$(Get-Date -Format 'HH:mm') - Her sey normal. Hata: $hataSayisi, Yavas istek: $yavasIstek"
}
Üçüncü Parti Araçlar Hakkında Kısa Not
Tamamen script tabanlı çözümler bazen yorucu olabiliyor. Eğer ekibinizde bütçe varsa:
- GoAccess: Windows’ta WSL üzerinden çalışır, gerçek zamanlı terminal tabanlı IIS log analizi yapar, hafif ve hızlıdır.
- Log Parser Studio: Microsoft’un ücretsiz aracı, SQL benzeri sorgularla IIS loglarını analiz etmenizi sağlar, büyük dosyalarda bile performanslıdır.
- Elastic Stack (ELK): Filebeat ile IIS loglarını Elasticsearch’e gönderin, Kibana üzerinden görsel dashboard oluşturun. Kurulumu zahmetli ama elde ettiğiniz görünürlük tartışmasız.
- Splunk: Kurumsal ortamlar için güçlü bir seçenek ama lisans maliyeti yüksek.
Bunların hiçbirine bütçe yoksa PowerShell ile yapabileceklerinizin sınırı çok uzakta.
Sonuç
IIS logları, web sunucunuzun size söylemeye çalıştığı ama kimsenin dinlemediği hikayelerdir. Bu yazıda anlattığım scriptleri bir “Log Analiz Araç Kutusu” olarak bir klasörde toplayın ve rutin kontroller için schedule’a alın. Proaktif log analizi, sorun büyümeden önce müdahale etme fırsatı verir.
Özetlemek gerekirse şu alışkanlıkları kazanmak önemlidir:
- Günlük: Hata sayısı ve ortalama response time’a bakın
- Haftalık: IP bazlı anormallik ve 404 storm kontrolü yapın
- Aylık: Log boyutlarını ve eski dosyaların temizliğini gözden geçirin
- Deploy sonrası: Mutlaka canlı log takibi yapın, en az 30 dakika
IIS log analizi bir kez alışkanlık haline geldiğinde, sorunlara reaksiyon göstermek yerine onları öngörmeye başlarsınız. Ve bu geçiş, iyi bir sysadmin ile harika bir sysadmin arasındaki ince çizginin tam kendisidir.
