Make love, not var_dump())

Captain's Log

Özgür Web Teknolojileri Günleri'nde tanıştığım İlyas Özgören bir sorundan bahsetti. İlyas bey, bir reklam networkü şirketinde çalışıyor ve müşterilerin sitelere ekledikleri JavaScript kodunun bazen çalışmadığını ve bunun genelde sayfadaki başka bir JavaScript hatasından kaynaklandığını söylüyordu. Bundan bir şekilde haberdar olmak istiyorlardı. Kısaca, tarayıcıda oluşan bir hatadan, sunucu tarafında haberdar olmak istiyorlardı. Ona orada kısaca yardımcı oldum, burada biraz daha detaylı şekilde yazayım

Hataları "loglamak", daha sonra "bu hata acaba neden olmuş?"u tespit etmek için en güzel yöntemlerden birisi. Bunu sunucu tarafında, backend işlerinde çoğunlukla kullanıyoruz. Yavaş çalışan bir SQL sorgusunu log'layıp daha sonra bu yavaş sorguları optimize ediyoruz, sunucumuza gelen istekleri log'layıp çeşitli optimizasyonlar yapıyoruz, çeşitli veriler ediniyoruz. Sunucu tarafında bu işi yapmanın, artık neredeyse standart haline gelmiş yöntemleri var. Bu konulara şimdilik girmeyeceğim. Bu yazı, tarayıcıda oluşan hataları nasıl log'layabileceğimiz ile alakalı olacak.

Client tarafında, geliştirme aşamasında, hata ayıklama yöntemlerini çokca kullanıyoruz. Kâh "alert()" ile kâh "console.log()" ile çeşitli çıktıları ayıklıyoruz. Biraz daha ileri gidip Firebug ile, ChromeDevTools ile veya Opera Dragonfly(♥) ile kodu debug ederek, break point'ler serpiştirerek geliştirmelerimizi yapıyoruz. Ama bu her zaman "mükemmel" ürünler çıkaracağımız anlamına gelmiyor. Ne kadar test etsek de gözümüzden kaçan hatalar olabiliyor ve bu hatalar son kullanıcı tarafında akışı olumsuz etkileyip bize para/prestij kaybettirebiliyor.

Mesela garip bir şekilde 2000px üzeri bir çözünürlükte garip bir JavaScript hatası oluşuyor olabilir, siz Array objesinin güzelim indexOf() methodunu, bu methodun Explorer 9 öncesinde desteklenmediğini unutup kullanmış olabilirsiniz. Veya -olmaz ya- garip bir şekilde Explorer 6 ile belli bir IP bloğundan gelen kullanıcıların JavaScript'lerinde bir hata oluşuyor olabilir. "Yuh o kadar da olur mu?" diyorsunuz, ama olabilir ;) Peki bu tip bir hata olursa, bundan nasıl haberdar oluyorsunuz? Normalde, kullanıcı sizi telefonla arayıp "ben ürün alacağım ama ödeme yapamıyorum yeaaa :(" diye telefon etmeden göremiyorsunuz. Genelde müşteriden de hangi çözünürlükte, hangi tarayıcı sürümü ile giriş yaptığı bilgisini almanız da mümkün olmuyor. Çoğu kişi tarayıcı sürümünü geçtim, hangi tarayıcıyı kullandığını bile bilmiyor ;). O zaman "bu işin daha teknik bir çözümü, tarayıcı tarafında oluşan hataları, olabildiğince detaylı bir şekilde kullanıcıya bildirecek bir sistem olmalı!!1" diye düşünmeye başlayabilirsiniz. İşte orada imdadınızıa "window.onerror" yetişiyor.

"window.onerror" Internet Explorer >= 5.5, Chrome >= 13, Opera >= 11.6, Firefox >= 6, Safari > 5.1 gibi günümüz "modern tarayıcılarının" tümünde çalışan bir kurtarıcı. window.onerror, yakalanmayan bir exception, bir runtime error veya bir derleme hatası olduğunda oluşan hatayı -bir event fırlatılmamasına rağmen- yakalayan bir "event handler". Kodun içinde bir şekilde atılan ve hiç bir try/catch bloğu içerisinde yakalanmayan exception'lar, koddaki syntax hataları, "same origin policy"ye aykırı yapılan istekler vs. window.onerror'da yakalanıyor.

Örneğin şöyle bir kodunuz olsun (çalışan örnek)
<a id="foo">ödeme yap</a>

<script type="text/javascript">
    $("#foo").click(function () {
        boyleBirMethodYok();
    });
</script>

Boyle bir kod normalde bir "compile" hatası vermez, orada bir "runtime error" var. #foo'ya tıklanınca "boyleBirMethodYok()" isimli bir methodu cagirmaya calisacak "runtime"da ama oyle bir methodu bulamayacak. Boyle bir kodu çalıştırıp, "ödeme yap"a tıkladığınızda hiçbir hata mesajı görmeyeceksiniz (error console'dan bahsetmiyorum), ve kullanıcı "neden ödeme yapamadığını anlayamayacak!", üstelik sizin böyle bir hata yaptığınızdan haberiniz de olmayacak.

Kodu şu şekilde değiştirirsek (çalışan örnek)
<a id="foo">ödeme yap</a>

<script type="text/javascript">
    /* artik en azindan kullanici bir yanlis oldugunu biliyor */
    window.onerror = function(message, url, lineNumber) {
        alert(url + " dosyasında + " + lineNumber + ".satırda bir hata oluştu : " + message);
    };
    
    $("#foo").click(function () {
        boyleBirMethodYok();
    });
</script>
artık, son kullanıcının bir şeylerin yanlış gittiğini anlayıp, bize telefon etmesini sağlayabileceğiz.

Peki bu tip hataları son kullanıcıdan alıp yazılım ekibimize bildirmesi için bir kaç kişi istihdam mı etmemiz gerekiyor? Tabii ki hayır.

Şöyle bir kod ile (çalışan örnek)
<a id="foo">ödeme yap</a>

<script type="text/javascript">
    
    window.onerror = function(message, url, lineNumber) {
       /**
        * artik olusan her hata "javascript-hatalari.php" dosyasına gönderilecek
        * bu dosya, sunucu tarafinda, bu hatalari istedigi gibi kaydeder
        * isterse e-posta olarak gonderir, isterse veritabanina yazar, isterse dosyaya yazar ...
        */
        $.post('/javascript-hatalari.php', {message: message, lineNumber: lineNumber, url: url});
    };
    
    $("#foo").click(function () {
        boyleBirMethodYok();
    });
</script>
artık bir hata oluştuğunda, bizim sunucumuzda bulunan bir dosyaya (örnekte /javascript-hatalari.php) bu bilgiler gönderilecek. Bu dosya, bu verileri istediği gibi log'layacak. İsterse ilgili birime e-posta atacak, isterse müdüre "bak senin front-end'cilerin böyle hatalar yapıyor" diye SMS atacak, isterse bu hatalar için hata takip sisteminde ticket'lar açacak... Bu hataları nasıl log'lamak istiyorsa öyle log'layacak, ekip yaptığı hatalardan bu şekilde çabucak haberdar olabilecek.

Oluşan hataların gittiği protokolün güvenliğine (http/https), giden verinin saklanma şekline (direkt web'den erişilebilecekb bir hata log'u iyi bir etki yaratmayabilir :p) dikkat ederseniz, artık, tarayıcı tarafında oluşan hataları sorunsuzca log'layabiliyor olacaksınız.

Not: Örneklerde DOM erişimi ve XHR isteklerini basit kılması için jQuery kullanıldı.