Apache CustomLog ile Gelişmiş Log Formatları

Log dosyalarına baktığında sadece tarih, IP adresi ve istek satırından ibaret standart bir format görüyorsan, Apache’nin sana sunduğu imkanların çok küçük bir kısmını kullanıyorsun demektir. CustomLog direktifi, Apache’yi adeta bir telemetri aracına dönüştürebilir. Hangi kullanıcı hangi sayfayı ne kadar sürede yükledi, SSL versiyonu neydi, upstream’den dönen yanıt ne kadarda sürdü, tüm bunları log satırına gömebilirsin. Bu yazıda sıfırdan ileri seviyeye CustomLog kullanımını gerçek dünya senaryolarıyla ele alacağız.

CustomLog ve LogFormat Direktiflerinin Temeli

Apache’de loglama iki direktifin birlikte çalışmasıyla şekillenir: LogFormat ve CustomLog.

LogFormat bir şablon tanımlar ve bu şablona bir takma ad verir. CustomLog ise bu şablonu kullanarak log dosyasını belirler. İlişki tam olarak şöyle:

# httpd.conf veya apache2.conf içinde
LogFormat "%h %l %u %t "%r" %>s %b" common
CustomLog /var/log/apache2/access.log common

Burada common bir takma addır. İstersen takma ad kullanmadan formatı doğrudan yazabilirsin ama bu yaklaşım büyük konfigürasyonlarda karmaşıklığa yol açar. Takma ad kullanmak hem okunabilirliği artırır hem de aynı formatı birden fazla VirtualHost’ta yeniden kullanmanı sağlar.

Apache’nin öntanımlı olarak sunduğu iki klasik format vardır:

  • common: Temel bilgileri içeren kısa format
  • combined: Common’a ek olarak Referer ve User-Agent içerir

Ama gerçek hayatta bu iki format çoğu zaman yeterli olmaz.

Format Değişkenleri: Ne İşe Yarar, Nasıl Kullanılır

Apache’nin log formatı değişkenleri % ile başlar. En çok kullanılanları şöyle sıralayabiliriz:

  • %h: İstemci IP adresi
  • %l: ident protokolüyle gelen kullanıcı adı (genelde - döner, kullanılmaz)
  • %u: HTTP authentication ile giriş yapan kullanıcı adı
  • %t: İstek zamanı, varsayılan format [day/month/year:hour:minute:second timezone]
  • %r: İstek satırının tamamı, örneğin GET /index.html HTTP/1.1
  • %>s: Son durum kodu (yönlendirmeler dahil son kod)
  • %s: İlk durum kodu
  • %b: Gönderilen byte sayısı, header hariç
  • %B: Gönderilen byte sayısı, 0 yerine 0 döner
  • %T: İsteğin tamamlanma süresi saniye cinsinden
  • %D: İsteğin tamamlanma süresi mikrosaniye cinsinden
  • %{ms}T: İsteğin tamamlanma süresi milisaniye cinsinden (Apache 2.4+)
  • %q: Query string, varsa ? ile birlikte
  • %U: URL path, query string hariç
  • %v: Canonical sunucu adı (ServerName)
  • %V: UseCanonicalName ayarına göre sunucu adı
  • %a: İstemci IP (RemoteAddr)
  • %A: Yerel IP adresi
  • %p: Sunucunun dinlediği port
  • %m: HTTP metodu (GET, POST, vb.)
  • %H: İstek protokolü (HTTP/1.1 gibi)
  • %I: Alınan toplam byte (mod_logio gerektirir)
  • %O: Gönderilen toplam byte (mod_logio gerektirir)

Header ve Çevre Değişkenleri

Belirli bir HTTP header’ını loglamak için şu syntax kullanılır:

  • %{Header-Adı}i: Gelen istek header’ı
  • %{Header-Adı}o: Giden yanıt header’ı
  • %{Değişken-Adı}e: Çevre değişkeni
  • %{Değişken-Adı}n: Modüller arası notlar (mod_rewrite notları gibi)

Örneğin %{X-Forwarded-For}i ile proxy arkasındaki gerçek IP’yi, %{User-Agent}i ile tarayıcı bilgisini, %{Referer}i ile nereden geldiğini loglayabilirsin.

Pratik Format Örnekleri

Performans Odaklı Format

Response sürelerini izlemek, yavaş sayfaları tespit etmek için bu formatı kullanabilirsin:

LogFormat "%h %t "%r" %>s %b %D "%{Referer}i" "%{User-Agent}i"" performance
CustomLog /var/log/apache2/performance.log performance

Burada %D mikrosaniye cinsinden yanıt süresini verir. 5 saniyenin üzerindeki istekleri bulmak için:

awk '$6 > 5000000 {print $0}' /var/log/apache2/performance.log

JSON Formatında Loglama

Elasticsearch, Loki veya Splunk gibi araçlara log besleyeceksen JSON formatı hayatını kolaylaştırır. Log parser yazmana gerek kalmaz:

LogFormat "{ "time": "%{%Y-%m-%dT%H:%M:%S%z}t", "remote_ip": "%a", "method": "%m", "uri": "%U", "query": "%q", "status": "%>s", "bytes_sent": "%B", "duration_ms": "%{ms}T", "referer": "%{Referer}i", "user_agent": "%{User-Agent}i", "host": "%v" }" json_log
CustomLog /var/log/apache2/access_json.log json_log

Bu format sayesinde Filebeat veya Fluentd ile bu logu Elasticsearch’e göndermek için hiçbir parsing kuralı yazman gerekmez.

Proxy Arkasında Gerçek IP Loglama

Load balancer veya CDN arkasında çalışan sunucularda %h değişkeni proxy IP’sini döner. Gerçek kullanıcı IP’sini loglamak için:

LogFormat "%{X-Forwarded-For}i %h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-Agent}i"" proxy_combined
CustomLog /var/log/apache2/access.log proxy_combined

Eğer Cloudflare kullanıyorsan X-Forwarded-For yerine CF-Connecting-IP header’ını kullanmalısın:

LogFormat "%{CF-Connecting-IP}i %h %t "%r" %>s %b "%{User-Agent}i"" cloudflare
CustomLog /var/log/apache2/access.log cloudflare

SSL Bilgilerini Loglama

Hangi TLS versiyonu ve cipher kullanıldığını loglamak güvenlik analizi için çok değerlidir. mod_ssl yüklü olması şarttır:

LogFormat "%h %t "%r" %>s %b "%{SSL_PROTOCOL}x" "%{SSL_CIPHER}x" "%{SSL_CLIENT_S_DN}x"" ssl_detailed
CustomLog /var/log/apache2/ssl_access.log ssl_detailed
  • %{SSL_PROTOCOL}x: TLSv1.2, TLSv1.3 gibi kullanılan protokol
  • %{SSL_CIPHER}x: Kullanılan cipher suite
  • %{SSL_CLIENT_S_DN}x: Mutual TLS kullanıyorsan istemci sertifika bilgisi

Bu loglarda TLSv1 veya TLSv1.1 kullanan istemcileri tespit etmek için:

grep '"TLSv1"' /var/log/apache2/ssl_access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -20

Koşullu Loglama

Her isteği loglamak zorunda değilsin. env= seçeneğiyle belirli koşullara göre loglama yapabilirsin. Bu hem disk alanı tasarrufu sağlar hem de gürültüyü azaltır.

Belirli Durum Kodlarını Ayrı Loglamak

# Sadece 4xx ve 5xx hatalarını ayrı dosyaya logla
CustomLog /var/log/apache2/errors_4xx.log combined env=is_4xx
CustomLog /var/log/apache2/errors_5xx.log combined env=is_5xx

# Bu direktifleri SetEnvIf ile birlikte kullan
SetEnvIf Request_URI ".*" !is_4xx
SetEnvIf Request_URI ".*" !is_5xx

Ama bu yeterli değil. Durum koduna göre çevre değişkeni atamak için mod_setenvif veya mod_rewrite kullanmak gerekir. Daha temiz bir yaklaşım şudur:

# .conf dosyasına ekle
CustomLog /var/log/apache2/access.log combined
CustomLog /var/log/apache2/error_requests.log combined "expr=%>s >= 400"

Apache 2.4 ile gelen expr= sözdizimi çok daha güçlüdür:

# 500'den büyük hataları logla
CustomLog /var/log/apache2/server_errors.log combined "expr=%>s >= 500"

# Belirli bir path için loglama
CustomLog /var/log/apache2/api_access.log combined "expr=req('Host') == 'api.example.com'"

# POST isteklerini ayrı logla
CustomLog /var/log/apache2/post_requests.log combined "expr=%m == 'POST'"

Health Check ve Monitoring Trafiğini Loglamadan Geçirmek

Kubernetes probe’larını, load balancer health check’lerini ve uptime monitor trafiğini loglardan elemak log analizini temizler:

SetEnvIf Request_URI "^/health$" no_log
SetEnvIf Request_URI "^/ping$" no_log
SetEnvIf Remote_Addr "10.0.0.0/8" no_log

CustomLog /var/log/apache2/access.log combined env=!no_log

VirtualHost Bazlı Log Konfigürasyonu

Birden fazla sanal sunucu barındıran bir makinede her site için ayrı log tutmak hem izolasyon sağlar hem de analizi kolaylaştırır:

<VirtualHost *:443>
    ServerName www.example.com
    ServerAlias example.com

    # Bu site için özel format tanımla
    LogFormat "%h %t "%r" %>s %b "%{Referer}i" "%{User-Agent}i" %D" example_fmt

    CustomLog /var/log/apache2/example_access.log example_fmt
    ErrorLog /var/log/apache2/example_error.log

    # Ek olarak merkezi log da tut
    CustomLog /var/log/apache2/combined_access.log combined
</VirtualHost>

<VirtualHost *:443>
    ServerName api.example.com

    # API için daha detaylı format
    LogFormat "{ "ts": "%{%s}t", "ip": "%a", "method": "%m", "path": "%U", "status": "%>s", "ms": "%{ms}T", "bytes": "%B" }" api_json

    CustomLog /var/log/apache2/api_access.log api_json
    ErrorLog /var/log/apache2/api_error.log
</VirtualHost>

Gerçek Dünya Senaryoları

Senaryo 1: E-ticaret Sitesinde Ödeme Sayfası Performans Takibi

Bir e-ticaret sitesinde ödeme sayfasının yavaşladığına dair şikayet var. Normal log formatında bu tür sorunları ayırt etmek zor. Şu konfigürasyonla checkout sayfaları için özel log açabiliriz:

SetEnvIf Request_URI "^/checkout" is_checkout
SetEnvIf Request_URI "^/payment" is_checkout
SetEnvIf Request_URI "^/cart" is_checkout

LogFormat "%h %{%Y-%m-%d %H:%M:%S}t "%r" %>s %b %{ms}T "%{X-Forwarded-For}i"" checkout_perf

CustomLog /var/log/apache2/checkout_performance.log checkout_perf env=is_checkout

Ardından analiz:

# 3 saniyenin üzerindeki checkout istekleri
awk '$6 > 3000 {print $0}' /var/log/apache2/checkout_performance.log | wc -l

# Ortalama yanıt süresi
awk '{sum+=$6; count++} END {print "Ortalama:", sum/count, "ms"}' /var/log/apache2/checkout_performance.log

# Saatlik dağılım
awk '{split($2, t, ":"); print t[1] ":00"}' /var/log/apache2/checkout_performance.log | sort | uniq -c

Senaryo 2: Güvenlik Analizi için Gelişmiş Log Formatı

Bir web uygulamasında brute force saldırısı veya SQL injection denemesi tespit etmek istiyorsun. Bu durumda User-Agent, Referer ve request body boyutunu loglamak önemli:

LogFormat "%h %t "%m %U%q %H" %>s %I %O "%{Referer}i" "%{User-Agent}i" "%{X-Forwarded-For}i"" security_log

CustomLog /var/log/apache2/security_access.log security_log
  • %I: Gelen toplam byte (mod_logio gerektirir)
  • %O: Giden toplam byte

POST isteğinde gönderilen veri büyüklüğü %I ile görünür. Anormal büyük POST istekleri potansiyel saldırı girişimi olabilir:

# 1MB üzerinde POST gönderen IP'leri bul
awk '$3 ~ /POST/ && $7 > 1048576 {print $1}' /var/log/apache2/security_access.log | sort | uniq -c | sort -rn

mod_logio’yu etkinleştirmek için:

# Ubuntu/Debian
sudo a2enmod logio
sudo systemctl reload apache2

# RHEL/CentOS
# /etc/httpd/conf.modules.d/ içinde logio.conf oluştur
echo "LoadModule logio_module modules/mod_logio.so" > /etc/httpd/conf.modules.d/00-logio.conf
sudo systemctl reload httpd

Senaryo 3: Pipe ile Logları Gerçek Zamanlı İşlemek

Apache’de log dosyasına yazmak yerine bir programa pipe edebilirsin. Bu özellik log rotation, gerçek zamanlı analiz veya uzak log sunucusuna gönderme için kullanılır:

# rotatelogs ile log rotation
CustomLog "|/usr/sbin/rotatelogs /var/log/apache2/access_%Y%m%d.log 86400" combined

# cronolog ile (kurulum gerektirebilir)
CustomLog "|/usr/sbin/cronolog /var/log/apache2/%Y/%m/%d/access.log" combined

# Gerçek zamanlı analiz scripti
CustomLog "|/usr/local/bin/realtime_log_processor.sh" combined

rotatelogs ile hem gün bazında bölme hem de boyut bazında bölme yapabilirsin:

# 100MB veya 24 saatte bir yeni dosya
CustomLog "|/usr/sbin/rotatelogs -n 7 /var/log/apache2/access.log 100M" combined

Log Formatı Doğrulama ve Test

Yeni bir format yazdıktan sonra Apache konfigürasyonunu test etmeden reload etme:

# Konfigürasyon sözdizimini kontrol et
sudo apache2ctl configtest
# veya
sudo httpd -t

# Hangi modüllerin yüklü olduğunu gör
apache2ctl -M | grep -E "log|logio"

# Canlı log takibi
tail -f /var/log/apache2/access.log

# JSON formatı geçerli mi kontrol et
tail -100 /var/log/apache2/access_json.log | python3 -c "import sys, json; [json.loads(l) for l in sys.stdin]" && echo "JSON formatı geçerli"

Timestamp Formatını Özelleştirmek

Varsayılan %t formatı [26/May/2024:14:32:01 +0300] şeklindedir. Log analiz araçlarının çoğu bu formatı parsele edebilir ama ISO 8601 formatı daha evrenseldir:

# ISO 8601 format
LogFormat "%{%Y-%m-%dT%H:%M:%S%z}t" sadece_zaman

# Unix timestamp (epoch) - bazı araçlar için çok kullanışlı
LogFormat "%{%s}t" epoch_time

# Kombine kullanım
LogFormat "%h %{%Y-%m-%dT%H:%M:%S%z}t "%r" %>s %b %{ms}T" modern_format
CustomLog /var/log/apache2/modern_access.log modern_format

Unix timestamp kullanmak log analizi için çok pratiktir. Zaman dilimi karmaşasından kurtulursun ve matematiksel işlem yapmak kolaylaşır:

# Son 1 saat içindeki istekler
ONEHOURAGO=$(date -d '1 hour ago' +%s)
awk -v cutoff="$ONEHOURAGO" '$2 > cutoff {print $0}' /var/log/apache2/modern_access.log

Logları Analiz Etmek için Hızlı Araçlar

Log formatın ne kadar iyi olduğu, ondan ne kadar bilgi çıkarabileceğinle ölçülür. Sık kullanılan analiz komutları:

# En çok istek atan IP'ler
awk '{print $1}' /var/log/apache2/access.log | sort | uniq -c | sort -rn | head -20

# En yavaş 20 istek (performance formatı kullanılıyorsa, %D 6. alanda)
sort -t' ' -k6 -rn /var/log/apache2/performance.log | head -20

# Durum kodu dağılımı
awk '{print $9}' /var/log/apache2/access.log | sort | uniq -c | sort -rn

# Saatlik istek sayısı
awk '{print $4}' /var/log/apache2/access.log | cut -d: -f2 | sort | uniq -c

# En çok 404 alan URL'ler
awk '$9 == 404 {print $7}' /var/log/apache2/access.log | sort | uniq -c | sort -rn | head -20

GoAccess aracı Apache loglarını görsel olarak analiz etmek için harika bir seçenek:

# Kurulum
sudo apt-get install goaccess  # Debian/Ubuntu
sudo yum install goaccess      # RHEL/CentOS

# Kombine format ile analiz
goaccess /var/log/apache2/access.log -c

# HTML rapor oluştur
goaccess /var/log/apache2/access.log --log-format=COMBINED -o /var/www/html/report.html

Sonuç

Apache CustomLog direktifi, doğru konfigüre edildiğinde bir sunucuyu kör bir kutudtan şeffaf ve analiz edilebilir bir sisteme dönüştürür. Standart combined formatıyla işe başlamak makul bir başlangıç noktasıdır ama production ortamında performans takibi, güvenlik analizi ve observability ihtiyaçları için özelleştirilmiş formatlara ihtiyaç duyarsın.

Önerilen pratik adımlar şöyle sıralanabilir: önce mevcut log formatına %{ms}T ekleyerek performans verisini toplamaya başla, ardından proxy arkasındaysan X-Forwarded-For header’ını dahil et, sağlık kontrolü ve monitoring trafiğini elemeyi unutma, ve eğer log analiz platformu kullanıyorsan JSON formatına geç. Bu adımların her biri sana somut operasyonel fayda sağlar.

Log formatı değişikliği production’da sudo apache2ctl configtest ile doğrulandıktan sonra sudo systemctl reload apache2 ile sıfır downtime ile uygulanabilir. Mevcut log dosyaları etkilenmez, sadece yeni istekler yeni formatla yazılmaya başlar.

Yorum yapın