Çoklu Log Dosyalarında Regex Tabanlı Örüntü İzleme ve Anlık Uyarı Mekanizması Oluşturma

Bir gece yarısı pager’ın çaldığını ve production ortamında ne olduğunu anlamaya çalışırken beş farklı log dosyasını aynı anda takip etmek zorunda kaldığınızı düşünün. İşte tam bu noktada “keşke bunu önceden kurmuş olsaydım” dediğiniz an geliyor. Bu yazıda, çoklu log dosyalarını gerçek zamanlı izlemek, anlamlı örüntüleri yakalamak ve anında uyarı almak için grep, awk ve sed üçlüsünü nasıl bir arada kullanabileceğinizi aktaracağım. Saf teoriden değil, gerçekte çalışan sistemlerde edinilmiş deneyimlerden.

Neden Regex Tabanlı Log İzleme?

Çoğu ekip Elasticsearch, Splunk ya da Loki gibi merkezi log çözümlerine hızla atlar. Bunların yeri ayrı elbette, ama bir sysadmin olarak şunu net söyleyeyim: o araçlar çalışmadığında ya da henüz kurulmamışken ne yapacaksınız? Ya da log gönderme pipeline’ı bozulduğunda?

Terminalden çalışan, bağımlılığı sıfır, her Linux dağıtımında hazır olan araçlarla güçlü bir izleme mekanizması kurmak hem beceri hem de özgürlük demek. Üstelik bu yöntemler, büyük araçların altında da zaten çalışıyor.

Temel Araçların Kısa Anatomisi

Detaylara girmeden önce üç silahımızı tanıyalım.

grep ile ilgili bu senaryoda en çok kullanacağımız parametreler:

  • -E: Genişletilmiş regex kullan (ERE)
  • -P: Perl uyumlu regex (PCRE), daha güçlü örüntüler için
  • -i: Büyük/küçük harf duyarsız eşleşme
  • -o: Sadece eşleşen kısmı göster, tüm satırı değil
  • -c: Eşleşen satır sayısını ver
  • -l: Sadece eşleşme bulunan dosya adlarını listele
  • –color=always: Renklendirmeyi pipe’da da koru

awk için kritik olanlar:

  • -F: Alan ayırıcı karakter belirle
  • NR: Toplam satır numarası
  • FNR: Dosya içindeki satır numarası
  • FILENAME: O an işlenen dosya adı
  • $0: Tüm satır, $1, $2: Alanlar

sed için sık başvurduklarım:

  • -n: Varsayılan çıktıyı sustur
  • -E: Genişletilmiş regex
  • p: Eşleşen satırı yazdır
  • s/pattern/replace/: Değiştirme işlemi
  • d: Satır sil

Çoklu Dosyaları Aynı Anda İzlemek

tail -f tek dosya için güzel ama çoklu dosyalar için yetersiz kalıyor. İşte ilk gerçek dünya senaryomuz: web sunucusu, uygulama ve veritabanı loglarını aynı anda izlemek.

tail -f /var/log/nginx/access.log 
         /var/log/myapp/application.log 
         /var/log/postgresql/postgresql.log | 
grep -E --line-buffered 
  "(ERROR|CRITICAL|FATAL|OOM|connection refused|timeout)"

Burada --line-buffered kritik. Pipe kullandığınızda grep çıktıyı buffer’lıyor ve satırlar gecikmeli geliyor. Bu parametre satır satır flush yapmasını sağlıyor, gerçek zamanlı izleme için şart.

Ama bu hâlâ ham. Hangi dosyadan geldiğini görmüyoruz. tail -f zaten başlık satırı basıyor ama grep geçince o başlıklar kayboluyor. Şu şekilde düzeltelim:

tail -f /var/log/nginx/access.log 
         /var/log/myapp/application.log 
         /var/log/postgresql/postgresql.log 2>&1 | 
awk --re-interval '
/^==> .* <==$/ { current_file = $0; next }
/(ERROR|CRITICAL|FATAL|OOM|connection refused|timeout)/ {
    printf "33[31m[ALERT]33[0m %sn  %snn", current_file, $0
}'

Bu awk bloğu hem dosya kaynağını takip ediyor hem de eşleşen satırları renkli olarak basıyor. tail -f dosya değişiminde ==> dosya_adi <== formatında başlık basıyor, biz de bunu yakalıyoruz.

Regex Örüntülerini Derinleştirmek

Basit kelime aramasının ötesine geçelim. Gerçek log analizinde karmaşık örüntüleri yakalamanız gerekiyor.

HTTP 5xx Hatalarını ve Yavaş İstekleri Birlikte Yakalamak

# Nginx access log formatı: IP - - [tarih] "METHOD /path HTTP/1.1" STATUS BYTES "ref" "ua" response_time
tail -f /var/log/nginx/access.log | 
grep -P --line-buffered 
  '(?:(?:5d{2})s+d+s+"[^"]*"s+"[^"]*"s+(?:[5-9]d{3}|d{5,}))|(?:4d{2}s+d+s+"[^"]*"s+"[^"]*"s+d+)'

Bu regex hem 5xx hataları hem de 5 saniyenin üzerindeki yanıt sürelerini yakalıyor. PCRE’nin lookahead/lookbehind özelliklerini kullanmadan da okunabilir bir şekilde yazılabilir ama production’da neyin çalıştığını bilmek önemli, bu yüzden test edin.

Daha okunabilir bir yaklaşım için parçalara bölelim:

PATTERN_5XX='"s(5[0-9]{2})s'
PATTERN_SLOW='[0-9]{5,}$'
PATTERN_AUTH_FAIL='(401|403)s'

tail -f /var/log/nginx/access.log | 
grep -E --line-buffered 
  "(${PATTERN_5XX}|${PATTERN_SLOW}|${PATTERN_AUTH_FAIL})" | 
awk '{
    if ($0 ~ /"[[:space:]]5[0-9]{2}[[:space:]]/) source="5XX_ERROR"
    else if ($NF+0 > 5000) source="SLOW_REQUEST"
    else source="AUTH_FAIL"
    
    cmd = "date +"%Y-%m-%d %H:%M:%S""
    cmd | getline timestamp
    close(cmd)
    
    printf "[%s] [%s] %sn", timestamp, source, $0
}'

Anlık Uyarı Mekanizması Kurmak

İzlemek güzel de uyarı almak başka. Ekrana bakmıyorsanız ne işe yarar? Şimdi bunu bir adım ileri taşıyalım.

Temel Uyarı Script’i

#!/bin/bash
# log_alerter.sh - Çoklu log izleme ve uyarı sistemi

# Konfigürasyon
ALERT_EMAIL="[email protected]"
SLACK_WEBHOOK="https://hooks.slack.com/services/XXXXX/YYYYY/ZZZZZ"
LOG_FILES=(
    "/var/log/nginx/error.log"
    "/var/log/myapp/application.log"
    "/var/log/auth.log"
    "/var/log/syslog"
)
ALERT_COOLDOWN=300  # Aynı hata için 5 dakika bekleme süresi
STATE_DIR="/tmp/log_alerter_state"
mkdir -p "$STATE_DIR"

# Uyarı gönderme fonksiyonu
send_alert() {
    local severity="$1"
    local source_file="$2"
    local message="$3"
    local alert_key=$(echo "${severity}_${source_file}" | md5sum | cut -d' ' -f1)
    local last_alert_file="${STATE_DIR}/${alert_key}"
    
    # Cooldown kontrolü
    if [[ -f "$last_alert_file" ]]; then
        local last_time=$(cat "$last_alert_file")
        local now=$(date +%s)
        if (( now - last_time < ALERT_COOLDOWN )); then
            return 0
        fi
    fi
    
    date +%s > "$last_alert_file"
    
    # Email gönder
    echo -e "Severity: ${severity}nDosya: ${source_file}nMesaj: ${message}" | 
        mail -s "[LOG-ALERT][${severity}] $(hostname)" "$ALERT_EMAIL" 2>/dev/null
    
    # Slack webhook
    local slack_payload=$(printf '{"text":"[%s] *%s*n`%s`nDosya: %s"}' 
        "$severity" "$(hostname)" "$message" "$source_file")
    curl -s -X POST -H 'Content-type: application/json' 
        --data "$slack_payload" "$SLACK_WEBHOOK" > /dev/null 2>&1
    
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] ALERT SENT: [$severity] $source_file: $message"
}

export -f send_alert
export ALERT_EMAIL SLACK_WEBHOOK STATE_DIR ALERT_COOLDOWN

# Ana izleme döngüsü
tail -F "${LOG_FILES[@]}" 2>/dev/null | 
awk --re-interval '
/^==> .* <==$/ {
    match($0, /==> (.*) <==/, arr)
    current_file = arr[1]
    next
}
/(CRITICAL|FATAL|OOM killer|segfault|kernel panic)/ {
    severity = "CRITICAL"
    system("send_alert " severity " " current_file " " $0)
}
/(ERROR|Exception|Traceback|FAILED|connection refused)/ {
    severity = "HIGH"
    system("send_alert " severity " " current_file " " $0)
}
/(WARNING|WARN|deprecated|timeout)/ {
    severity = "MEDIUM"
    # Sadece logla, uyarı gönderme
    print "[WARN]", current_file, $0
}
'

tail -F (büyük F) kullanmaya dikkat edin, -f ile karıştırmayın. Büyük -F dosya rotate edilse bile yeni dosyayı takip etmeye devam eder. Logrotate kullanan sistemlerde hayat kurtarıcı.

Awk ile Zaman Penceresi Analizi

Anlık eşleşmeler yetmez bazen. “Son 5 dakikada 10’dan fazla hata olduysa uyar” mantığı kurmak gerekiyor. Rate-based alerting denen bu yaklaşımı saf awk ile implement edelim:

tail -f /var/log/myapp/application.log | 
awk '
BEGIN {
    error_count = 0
    window_start = systime()
    window_size = 300  # 5 dakika
    threshold = 10
}
/ERROR/ {
    current_time = systime()
    
    # Pencere dışına çıktıysak sıfırla
    if (current_time - window_start > window_size) {
        error_count = 0
        window_start = current_time
    }
    
    error_count++
    
    if (error_count >= threshold) {
        printf "[RATE ALERT] Son %d saniyede %d hata! Son hata: %sn", 
            window_size, error_count, $0
        # Sayacı sıfırla (spam'ı önle)
        error_count = 0
        window_start = current_time
    }
}
'

Bu yaklaşımın bir eksikliği var: systime() fonksiyonu gawk’ta çalışır, mawk’ta çalışmaz. Sisteminizde hangi awk olduğunu kontrol edin (awk --version). Ubuntu’da genellikle mawk varsayılan olarak gelir, gerekirse gawk kurun.

Sed ile Log Temizleme ve Normalleştirme

Farklı uygulamalar farklı log formatları kullanır. Uyarı mesajlarını anlamlı kılmak için sed ile normalleştirme yapabiliriz:

#!/bin/bash
# Farklı log formatlarını standart forma çevir

normalize_log() {
    sed -E '
        # ISO 8601 tarih formatına çevir (nginx formatından)
        s|[([0-9]{2})/([A-Za-z]{3})/([0-9]{4}):([0-9:]+) [+-][0-9]+]|[3-2-1 4]|g
        
        # Java stack trace başlangıcını işaretle
        s|^(at [a-z].*(.*:[0-9]+))$|  STACKTRACE: 1|
        
        # IP adreslerini hashle (GDPR için)
        s|([0-9]{1,3}.){3}[0-9]{1,3}|[IP_MASKED]|g
        
        # Şifre/token pattern temizle
        s|(password|token|secret|key)=[^&s"]+|1=[REDACTED]|gi
        
        # Boş satırları sil
        /^[[:space:]]*$/d
    '
}

tail -f /var/log/myapp/application.log | normalize_log | 
grep -E --line-buffered "(ERROR|CRITICAL|WARNING)"

Bu script özellikle güvenlik açısından kritik. Log uyarılarınız Slack’e ya da e-postaya gidiyorsa, içinde şifre veya token geçen satırları olduğu gibi göndermek istemezsiziniz.

Çoklu Sunucu İçin SSH Tabanlı Merkezi İzleme

Tek sunucu için yaptıklarımızı bir SSH tüneli ile birden fazla sunucuya genişletelim:

#!/bin/bash
# multi_server_monitor.sh

SERVERS=("web01.iç.sirket.com" "web02.iç.sirket.com" "db01.iç.sirket.com")
REMOTE_LOG="/var/log/syslog"
ALERT_PATTERN="(OOM|CRITICAL|kernel panic|disk full|No space left)"

for server in "${SERVERS[@]}"; do
    ssh -o ConnectTimeout=5 
        -o ServerAliveInterval=30 
        -o ServerAliveCountMax=3 
        -T "$server" 
        "tail -F ${REMOTE_LOG} 2>/dev/null" | 
    grep -E --line-buffered "$ALERT_PATTERN" | 
    sed "s/^/[${server}] /" &
done

# Tüm arka plan işlemlerini bekle ve Ctrl+C ile temiz çıkış sağla
trap 'echo "Monitoring durduruldu"; kill $(jobs -p) 2>/dev/null; exit 0' INT TERM
wait

Bu script her sunucu için ayrı bir SSH bağlantısı açıyor ve hepsinin çıktısını aynı terminale akıyor. & ile arka plana alıyoruz, trap ile Ctrl+C’de tüm child process’leri temizliyoruz.

Bir not: Bu yaklaşımda SSH key authentication şart. Password ile çalışmaz çünkü etkileşimli terminal yok. ssh-copy-id ile keylerinizi dağıtın.

Logrotate ile Uyumlu Çalışmak

Üretim sistemlerinde logrotate kaçınılmaz. Log dosyası döndürüldüğünde izleme script’inizin ne yapacağı önemli. Bunun için hem tail -F kullanımı hem de inotifywait tabanlı bir yaklaşım göstereyim:

#!/bin/bash
# inotify tabanlı log izleme (inotify-tools paketi gerekli)
# apt install inotify-tools veya yum install inotify-tools

LOG_DIR="/var/log/myapp"
PATTERN="(ERROR|CRITICAL|EXCEPTION)"

inotifywait -m -e modify,create,moved_to 
    --format '%w%f' 
    "$LOG_DIR" 2>/dev/null | 
while read -r filepath; do
    # Sadece .log uzantılı dosyaları izle
    [[ "$filepath" =~ .log$ ]] || continue
    
    # Dosyaya yeni eklenen satırları oku
    tail -n 1 "$filepath" | 
    grep -E "$PATTERN" | 
    while read -r line; do
        echo "[$(date '+%H:%M:%S')] [${filepath##*/}] $line"
        # Uyarı mekanizmasını buraya bağlayın
    done
done

inotifywait yaklaşımı polling’den çok daha verimli. Disk I/O ve CPU kullanımı açısından tail -f‘in sürekli polling yapmasına kıyasla çok daha hafif.

Performans ve Tuning Notları

Bu sistemleri kurduğunuzda birkaç kritik noktaya dikkat edin:

  • Regex karmaşıklığı CPU’yu yorar: Çok sayıda alternasyon içeren uzun regex’ler yüksek trafikli log dosyalarında CPU spike’ına neden olabilir. Önce basit fgrep (sabit string) ile filtreleyin, sonra regex uygulayın.
  • Buffer boyutları: tail -f ile pipe kullanımında Linux varsayılan olarak 4KB buffer kullanır. Yüksek trafikli log’larda stdbuf -oL tail -f ... ile satır bazlı buffering’e geçin.
  • Dosya descriptor limitleri: Çok sayıda log dosyasını aynı anda takip ediyorsanız ulimit -n değerini kontrol edin. Varsayılan 1024 büyük ortamlarda yetmeyebilir.
  • Cooldown mekanizması olmadan flapping: Bir hata tetiklenmeye başladığında saniyede yüzlerce uyarı gönderebilir. Yukarıdaki script’teki cooldown mantığını mutlaka uygulayın ya da geliştirin.
  • grep –line-buffered unutmayın: Pipe zincirlerinde bu parametreyi atlamak, satırların buffer’da birikmesine ve gerçek zamanlı izlemenin bozulmasına yol açar.

Bir de şunu söyleyeyim: Bu sistemleri screen ya da tmux session’ı içinde çalıştırın. SSH bağlantınız koptuğunda monitoring de duruyor, bu kabul edilemez. Daha iyi çözüm için script’i systemd service olarak tanımlayın.

# /etc/systemd/system/log-monitor.service
[Unit]
Description=Regex tabanlı log izleme servisi
After=network.target

[Service]
Type=simple
ExecStart=/opt/monitoring/log_alerter.sh
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

systemctl enable --now log-monitor ile hem şu an başlatın hem de boot’ta otomatik başlamasını sağlayın.

Sonuç

grep, awk ve sed üçlüsü, doğru kurgulandığında kurumsal monitoring araçlarının yokluğunda bile sizin için değerli bir güvenlik ağı oluşturuyor. Burada anlattıkların hiçbiri sihir değil; hepsi çoğu Linux sisteminde varsayılan olarak hazır araçlar. Önemli olan doğru regex’i yazmak, cooldown mekanizmasıyla uyarı yorgunluğunu önlemek ve log rotation gibi gerçek dünya durumlarına karşı hazırlıklı olmak.

Bu script’leri olduğu gibi değil, kendi ortamınıza göre uyarlayarak kullanın. Her uygulamanın log formatı farklı, her ekibin eşiği farklı. Regex’lerinizi önce grep -E "pattern" logfile.log ile statik dosya üzerinde test edin, sonra canlı ortama alın. Ve mutlaka false positive oranını takip edin; çok gürültülü bir alert sistemi, hiç olmamaktan daha kötü olabiliyor.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir