Bir hatayla karşılaşan geliştiricinin ilk refleksi genellikle kod içine birkaç print satırı serpiştirmektir. Değişkenin değerini ekrana bas, fonksiyonun çağrılıp çağrılmadığını kontrol et, sorunu bul, sonra o satırları sil. Küçük ölçekli sorunlarda bu yöntem işe yarar, ama kalıcı değildir; bir sonraki hata geldiğinde aynı işlemi baştan yapmak gerekir. Üstelik bu geçici çıktılar hatanın oluştuğu anı gösterir, ama o ana nasıl gelindiğini çoğu zaman göstermez.
Log kayıtları tam olarak bu boşluğu doldurmak için vardır. İyi kurgulanmış bir loglama sistemi, sadece "hata burada oluştu" bilgisini değil, hataya giden adımların kronolojik izini de saklar. Bu da hata ayıklamayı, karanlıkta bir şey arama sürecinden, bırakılmış izleri takip etme sürecine dönüştürür. Bu yazıda logların bu şekilde sistematik bir araca dönüşmesi için nelere dikkat edilmesi gerektiğine bakacağız.
Bir bakışta
- Log kayıtları, hataya giden adımların kronolojik izini saklayarak sistematik hata ayıklama sağlar.
- Log seviyeleri doğru kullanılmazsa kritik hatalar bilgi yığını içinde kaybolabilir.
- Aşırı loglama performans kaybına, gürültüye ve hassas veri sızıntısına yol açabilir.
Log Kayıtları Neden Sadece Hata Anını Değil, Olay Zincirini Gösterir
print ile debug yapmanın en büyük sınırlılığı, bilginin geçici ve bağlamdan yoksun olmasıdır. Ekrana basılan bir değer, o anki çalıştırmada neyin doğru neyin yanlış gittiğini gösterebilir, ama bir saat önce üretim ortamında oluşan ve artık tekrar üretilemeyen bir hatayı açıklamaz. Çünkü o bilgi hiçbir yerde kalıcı olarak durmuyordur.
Log kayıtlarının amacı burada devreye girer: sistemin zaman içindeki davranışını kalıcı bir şekilde kaydetmek. Bir kullanıcı işlemi başlattığında, hangi fonksiyonların çağrıldığı, hangi verilerin işlendiği, hangi kararların alındığı sırayla log dosyasına yazılırsa, hata oluştuğunda geriye dönüp bu zinciri okumak mümkün olur. Hatanın kendisi genellikle sonuçtur; asıl önemli olan o sonuca götüren adımlar dizisidir.
Bu bakış açısıyla loglama, hata ayıklamayı bir "kayıp arama" faaliyetinden bir "iz sürme" faaliyetine dönüştürür. Kayıp arayan kişi elindeki tek ipucuyla rastgele yerlere bakar; iz süren kişi ise bıraktığı işaretleri takip ederek olayın nasıl geliştiğini adım adım yeniden kurar.
Log Seviyeleri: Hangi Bilgi Ne Zaman Kaydedilmeli
Neredeyse tüm loglama kütüphaneleri, mesajları önem derecesine göre sınıflandıran seviyeler sunar. Bu seviyelerin anlamını doğru kavramak, hem loglama yaparken hem de logları okurken işi kolaylaştırır.
- debug: Geliştirme sürecinde işe yarayan, ayrıntılı ve teknik bilgiler. Bir fonksiyona hangi parametrelerle girildiği, ara hesaplama sonuçları gibi bilgiler bu seviyede tutulur.
- info: Sistemin normal akışında olan, ama takip edilmesi faydalı olaylar. Örneğin bir kullanıcının oturum açması veya bir işlemin başarıyla tamamlanması.
- warning: Henüz hataya dönüşmemiş ama dikkat edilmesi gereken durumlar. Beklenen bir değerin sınırın dışında olması gibi.
- error: Bir işlemin tamamlanamadığı, ancak sistemin genel olarak çalışmaya devam edebildiği durumlar.
- critical: Sistemin bütününü etkileyen, acil müdahale gerektiren ciddi arızalar.
Bu ayrımın önemi, yanlış seviye seçildiğinde ortaya çıkan sorunlarda daha net görülür. Her şey info seviyesinde loglanırsa, gerçekten kritik bir hata bu bilgi yığınının içinde kaybolabilir. Tersine, her küçük durum error olarak işaretlenirse, log dosyasını okuyan kişi hangi kaydın gerçekten acil olduğunu ayırt edemez hale gelir.
Pratikte geliştirme sürecinde genellikle debug ve üzeri seviyeler izlenirken, üretim ortamında günlük olarak info ve üzeri seviyelere odaklanmak, error ve critical kayıtlarına ise öncelik vermek makul bir yaklaşımdır.
Anlamlı Bir Log Mesajı Nasıl Yazılır
"Hata oluştu" gibi bir log mesajı, teknik olarak bir kayıt bırakmış olsa da pratikte neredeyse hiçbir işe yaramaz. Böyle bir mesaj, hangi hatanın, nerede, hangi koşullarda oluştuğu sorularının hiçbirine cevap vermez. Anlamlı bir log mesajının barındırması gereken birkaç temel unsur vardır:
Zaman damgası: Çoğu loglama kütüphanesi bunu otomatik ekler, ama bu bilginin doğru formatta ve tutarlı olması, olayların sıralamasını doğru kurmak için kritiktir. Özellikle birden fazla sürecin aynı anda çalıştığı sistemlerde, hangi olayın hangisinden önce gerçekleştiğini anlamak zaman damgasına bağlıdır.
Bağlam bilgisi: Mesajın hangi kullanıcıya, hangi isteğe, hangi fonksiyon veya modüle ait olduğu belirtilmelidir. Örneğin "Ödeme işlemi başarısız" yerine "Ödeme işlemi başarısız: kullanıcı_id=1234, istek_id=abc-987, fonksiyon=odeme_isle" gibi bir mesaj, aynı türden yüzlerce hata arasında ilgili olanı bulmayı kolaylaştırır.
Değişken değerleri: Hatanın oluştuğu andaki ilgili değişkenlerin değerleri log mesajına eklenirse, sorunu yeniden üretmek çok daha kolay hale gelir. Bir hesaplama hatası varsa, girdi olarak kullanılan değerlerin loglanması, kodu tekrar çalıştırmadan bile hatanın kaynağı hakkında fikir verebilir.
Bu üç unsur bir araya geldiğinde, log mesajı sadece "bir şey oldu" demek yerine "şu bağlamda, şu değerlerle, şu sonuç ortaya çıktı" bilgisini taşır. Bu da log dosyasını aranabilir ve anlamlı bir kayıt haline getirir.
Aşırı Loglamanın Getirdiği Riskler
Loglamanın faydalı olması, her şeyi loglamak gerektiği anlamına gelmez. Aşırı loglama, kendi başına bir sorun kaynağı olabilir.
Performans kaybı: Özellikle sık çalışan döngüler veya yüksek trafikli fonksiyonlar içinde her adımı loglamak, sistemin genel performansını gözle görülür şekilde düşürebilir. Disk yazma işlemleri ve log dosyalarının büyümesi, özellikle kaynak kısıtlı ortamlarda maliyetli hale gelebilir.
Gürültü problemi: Log dosyası binlerce satır gereksiz veya rutin bilgiyle doluysa, asıl önemli olan hata kaydı bu gürültünün içinde kaybolabilir. Bu durumda loglama, sorunu çözmek yerine sorunu bulmayı zorlaştıran bir engele dönüşür.
Hassas veri riski: Belki de en ciddi risk budur. Kullanıcı şifreleri, kimlik bilgileri, ödeme bilgileri veya oturum token'ları yanlışlıkla log mesajına dahil edilirse, bu bilgiler log dosyalarına erişimi olan herkes tarafından görülebilir hale gelir. Bu durum hem gizlilik hem de güvenlik açısından ciddi bir açık oluşturur. Bu nedenle loglama yapılırken hangi verinin hiçbir koşulda kayıt altına alınmaması gerektiği net bir şekilde belirlenmelidir [kaynak gerekli].
Bu risklerin ortak çözümü, loglamayı bilinçli bir tasarım kararı olarak ele almaktır: her log satırının kime, ne zaman, hangi amaçla fayda sağlayacağını düşünmek.
Bir Hatayı Loglardan Takip Ederken İzlenecek Mantık
Bir hata raporu geldiğinde, log dosyasını baştan sona okumak yerine sistematik bir yaklaşım izlemek zaman kazandırır. Genel olarak izlenebilecek mantık şu şekildedir:
Öncelikle hatanın oluştuğu son kayda, yani genellikle error veya critical seviyesindeki mesaja odaklanılır. Bu kayıt, sorunun "nerede" patladığını gösterir ama "neden" patladığını her zaman açıklamaz. Bu noktadan geriye doğru okuma yapılır: aynı bağlam bilgisine (kullanıcı kimliği, istek kimliği gibi) sahip önceki kayıtlar sırayla incelenir.
Bu geriye doğru okuma sırasında amaç, olayların zincirini yeniden kurmaktır. Hangi adımda beklenen değer beklenmeyen bir hale geldi? Hangi fonksiyon çağrısından sonra sistem farklı bir yola girdi? Bu sorulara cevap ararken, error seviyesindeki kayıttan başlayıp, aynı bağlama ait debug seviyesindeki daha ayrıntılı kayıtlara geçmek faydalı olur. Üretim ortamında debug seviyesi kapalıysa bile, geliştirme ortamında hatayı yeniden üretip bu ayrıntılı kayıtlara ulaşmak mümkün olabilir.
Bu yöntem, rastgele kod satırlarını inceleyip tahmin yürütmek yerine, elde somut bir kanıt zinciriyle çalışmayı sağlar.
Dağıtık Sistemlerde İz Sürme ve Korelasyon Kimlikleri
Tek bir uygulama sunucusunda çalışan basit sistemlerde log takibi nispeten kolaydır. Ancak bir isteğin birden fazla servis tarafından işlendiği dağıtık mimarilerde durum karmaşıklaşır. Bir kullanıcı isteği, örneğin önce bir kimlik doğrulama servisinden, sonra bir işlem servisinden, ardından bir bildirim servisinden geçebilir. Her servis kendi log dosyasına kendi kayıtlarını yazar ve bu kayıtlar birbirinden bağımsız görünür.
Bu sorunu çözmek için kullanılan yaklaşım, her isteğe başlangıçta benzersiz bir korelasyon kimliği (request ID veya trace ID olarak da adlandırılır) atamak ve bu kimliği isteğin geçtiği her servise, her log satırına taşımaktır. Böylece farklı servislerdeki log dosyaları ayrı ayrı tutulsa bile, aynı korelasyon kimliğine sahip kayıtlar bir araya getirilerek isteğin uçtan uca yolculuğu yeniden kurulabilir.
Bu yaklaşım özellikle mikroservis tabanlı mimarilerde hata ayıklamayı büyük ölçüde kolaylaştırır. Korelasyon kimliği olmadan, bir isteğin hangi serviste takıldığını bulmak neredeyse tahmine dayalı bir süreç haline gelirken, bu kimlikle birlikte ilgili tüm kayıtlar filtrelenip zaman sırasına göre incelenebilir.
Logları Merkezi Bir Yerde Toplamanın Faydası
Birden fazla sunucu veya servisin ürettiği log dosyalarını tek tek makinelere bağlanıp okumak, hem zaman kaybettirir hem de büyük ölçekli sistemlerde pratik olarak sürdürülemez hale gelir. Bir hatanın hangi sunucuda oluştuğunu bilmeden ilgili log dosyasını bulmak, kendi başına bir arama sorununa dönüşebilir.
Bu nedenle birçok ekip, üretilen logları merkezi bir yerde toplayan yaklaşımlar benimser. Bu tür sistemler genel olarak, farklı kaynaklardan gelen log kayıtlarını tek bir arayüzde birleştirir; anahtar kelimeye, zaman aralığına, log seviyesine veya korelasyon kimliğine göre arama ve filtreleme imkânı sunar. Bu sayede bir geliştirici, hangi sunucuda hangi dosyanın bulunduğunu bilmeden, doğrudan "şu istek kimliğine ait tüm kayıtları göster" gibi bir sorguyla ilgili bilgiye ulaşabilir.
Merkezi loglamanın bir diğer faydası da ekip içi paylaşımı kolaylaştırmasıdır. Bir hatayı araştıran kişi, bulduğu log kayıtlarını doğrudan ekip arkadaşlarıyla paylaşabilir; herkes aynı merkezi kaynağa erişerek aynı veriyi görebilir. Bu, hata ayıklama sürecini bireysel bir uğraştan, ekip çapında koordine edilebilen bir sürece dönüştürür.
Geliştirme Ortamı ile Üretim Ortamında Log Stratejisi Farkı
Loglama stratejisi, uygulamanın çalıştığı ortama göre farklılaşmalıdır. Geliştirme ortamında performans kaygısı görece düşüktür ve amaç, kodun davranışını olabildiğince ayrıntılı görebilmektir. Bu nedenle debug seviyesinde, bol miktarda ayrıntı içeren loglama burada genellikle kabul edilebilir, hatta faydalıdır.
Üretim ortamında ise durum farklıdır. Yüksek trafik altında her ayrıntıyı loglamak performansı olumsuz etkileyebilir, log dosyalarının boyutu hızla büyüyebilir ve daha önce bahsedilen hassas veri riskleri daha büyük bir önem kazanır. Bu ortamda genellikle info ve üzeri seviyelere odaklanmak, debug seviyesini yalnızca gerektiğinde geçici olarak açmak daha makul bir yaklaşımdır.
Bu iki ihtiyaç arasında denge kurmanın pratik yolu, log seviyesini koddan bağımsız, yapılandırma üzerinden değiştirilebilir hale getirmektir. Böylece üretimde bir sorun yaşandığında, kodu yeniden derlemeden veya dağıtmadan sadece yapılandırmayı değiştirerek geçici olarak daha ayrıntılı loglama açılabilir, sorun tespit edildikten sonra tekrar normal seviyeye dönülebilir. Bu esneklik, iki ortamın farklı ihtiyaçlarını tek bir sistemle karşılamanın en pratik yollarından biridir.
Sonuç olarak, log kayıtları sadece hata anını değil, o hataya götüren tüm yolculuğu görünür kılan bir araçtır. Doğru seviye seçimi, anlamlı mesaj yazımı, aşırı loglamadan kaçınma ve dağıtık sistemlerde korelasyon kimlikleri gibi pratikler bir araya geldiğinde, hata ayıklama süreci tahmine dayalı bir uğraş olmaktan çıkıp, kanıta dayalı ve sistematik bir çalışmaya dönüşür.



