Loglarımda WARN ile ERROR seviyelerini ne zaman kullanacağıma dair net bir kural nasıl belirlerim?
Soru
Laravel API'lerimiz ve Go servislerimiz neredeyse her şeyi ERROR seviyesinde logluyor — yakalanan exception'lar, başarısız validation'lar, retry'lı HTTP çağrıları, hatta beklenen 404'ler bile ERROR olarak düşüyor. Sonuçta Sentry ve alert kanallarımız tamamen gürültüye döndü; gerçek bir sorun çıktığında binlerce anlamsız ERROR arasında kayboluyor. WARN ile ERROR arasında, ekibin tutarlı uygulayabileceği net ve uygulanabilir bir sınırı nasıl çizerim?
Cevap
Kısa cevap: Seviyeyi “log satırının içeriği” değil, “birinin harekete geçmesi gerekiyor mu” sorusu belirlemeli. ERROR = kırdığınız bir söz; WARN = sistemin kendi kendine toparladığı beklenmedik bir durum.
-
Eksen aksiyon alınabilirliktir. ERROR, bir isteğin veya job’ın sizin hatanızla başarısız olduğu ve birinin bakması gereken durumdur. WARN ise beklenmedik ama sistemin tolere ettiği bir şeydir: retry tuttu, fallback devreye girdi, soft limit’e yaklaşıldı. “Bu satır gece 3’te fire olsa birini uyandırır mıyım?” sorusuna evet diyorsanız ERROR’dır.
-
Beklenen hatayı bug’dan ayırın. 404, validation hatası, kullanıcının yanlış girdisi — bunlar sizin kodunuzun kırılması değil, INFO ya da en fazla WARN’dır. Kaba kural: 4xx client’ın sorunudur, 5xx sizindir. ERROR’ı yalnızca kendi kodunuz verdiği sözü tutamadığında kullanın.
-
Retry ve fallback’i doğru sınıflayın. Tekrar denenecek tek bir başarısız deneme WARN’dır; geçici bir titreme aksiyon gerektirmez. Ama tüm retry’lar tükendiğinde veya mesaj dead-letter’a düştüğünde artık ERROR’dır — çünkü iş kalıcı olarak yarım kaldı.
-
Alert’i yalnızca ERROR’a bağlayın, onu da oranla. Uyarılarınız ERROR (ve fatal) üzerinden tetiklensin; tek satıra değil eşik/oran üzerinden (“5 dakikada 50 ERROR”). WARN dashboard’u besler, pager’ı değil.
-
Aynı hatayı beş kez loglamayın. Go’da her katmanda hem error return edip hem loglarsanız aynı başarısızlık beş satır olur. Kararın verildiği sınırda (handler) bir kez loglayın; alt katmanlar sadece error’ı wrap edip döndürsün. Laravel’de aynısı için
Log::error’ı her yere serpmek yerine exception handler’dakireport()seviyesini kullanın. -
ERROR ile FATAL/critical’ı da ayırın ve her ERROR’ı aksiyon alınabilir yapın. FATAL, sürecin devam edemediği durumdur (config eksik, boot başarısız); ERROR ise bir isteğin/job’ın başarısızlığıdır, süreç ayaktadır. Ayrıca her ERROR satırı, birinin gecenin bir yarısı bakacağını varsayarak yeterli context taşımalı: request/trace id, ilgili kimlikler ve wrap’lenmiş hata zinciri. Aksiyon alınamayan bir ERROR zaten ERROR değildir.
Küçük bir örnek — seviyeyi satırın kaynağı değil sonucu belirlesin:
if err := publish(ctx, msg); err != nil {
if attempt < maxRetries {
log.Warn("publish failed, will retry", "attempt", attempt, "err", err)
return retry
}
// retry'lar tükendi: artık gerçekten aksiyon gerekiyor
log.Error("publish failed permanently, sent to DLQ", "err", err)
}
Sonuç: Ben olsam ekibe tek cümlelik bir kural verirdim: “Bu log gece 3’te fire olduğunda birinin uyanması gerekiyorsa ERROR, gerekmiyorsa WARN.” Buna ek olarak hatayı yalnızca sınırında bir kez loglama disiplinini koyar, alert’leri ERROR oranına bağlardım. Bu iki değişiklik gürültünün çoğunu tek başına eritir; kalanını da birkaç hafta boyunca yanlış seviyedeki logları düzelterek temizlersiniz. Amaç sıfır ERROR değil; her ERROR’ın gerçekten bir anlam taşıması.
Yorumlar
Yorum yapmak için GitHub hesabınızla giriş yapmanız yeterli. Yorumlar GitHub Discussions üzerinde saklanır.