Tarayıcıda kullanılan hata ayıklama teknikleri
(alan.norbauer.com)- Tarayıcı geliştirici araçlarını orta seviye ve üzeri düzeyde kullanabiliyorsanız, koşullu breakpointleri basit durdurma koşullarının ötesine taşıyıp günlükleme, izleme, durum değiştirme ve zaman ölçümü aracı olarak genişletebilirsiniz
console.log,console.count,console.trace,console.timegibi konsol API'lerini breakpoint'e veya Watch Pane'e koyarsanız, yürütmeyi durdurmadan akışı ve durumu kaydedebilirsiniz- Fonksiyon argüman sayısı, zaman, CSS hesaplanmış değerleri, çalıştırma sayısı, rastgele örnekleme ve global boolean'ları birleştirerek durma anını çok daha hassas biçimde kontrol edebilirsiniz
- Chrome'un
monitor,debug,copy,$0,getEventListeners,monitorEventskomutları; fonksiyon çağrıları, DOM, event'ler ve pano işlemlerini konsoldan hızlıca yönetmenizi sağlar - DOM'un JS veya kullanıcı event'leriyle sürekli değiştiği ekranlarda JS'yi duraklatmak,
setTimeout(debugger), DOM snapshot'ları ve odak takibi; HTML/CSS hata ayıklama için özellikle faydalıdır
Koşullu breakpoint'leri hata ayıklama aracına dönüştürmek
-
Koşullu breakpoint'lere yalnızca doğru/yanlış koşulları değil, yan etkisi olan JavaScript ifadeleri de koyabilirsiniz
-
Bunu kullanarak normal breakpoint'leri günlükleme, izleme, durum değiştirme ve zaman ölçümü aracı gibi kullanabilirsiniz
-
Logpoint ve tracepoint
- Koşullu breakpoint içinde
console.logçağırırsanız, yürütmeyi durdurmadan konsola değer bırakan bir logpoint gibi davranır console.countkullanırsanız ilgili satırın kaç kez çalıştığını da sayabilirsiniz- Mayıs 2020 güncellemesi itibarıyla başlıca tarayıcılar logpoint/tracepoint'i doğrudan destekliyor
- Koşullu breakpoint içinde
-
Watch Pane'de konsol API kullanımı
- Watch Pane'e de
console.loggibi ifadeler ekleyebilirsiniz - Uygulama debugger'da her durduğunda
console.table(localStorage)ile localStorage snapshot'ı alabilirsiniz - DOM değişiklik breakpoint'i ile Watch ifadelerini birlikte kullanırsanız, DOM alt ağacı değişikliklerinden sonra snapshot'ları bir diziye kaydedebilirsiniz
(window.doms = window.doms || []).push(document.documentElement.outerHTML)- DOM değişiklik breakpoint'leri yürütmeyi durdurmayacak şekilde ayarlanamaz
- Watch Pane'e de
-
Çağrı yığınını izleme
- Yükleme spinner'ını gösteren ve gizleyen fonksiyonlar varken yalnızca gösterme çağrılarının kaldığı sorunu ararken,
console.trace'i koşullu breakpoint'e koyabilirsiniz - Gösterme fonksiyonunun son stack trace'inde çağıranı tıklayarak ilgili çağrı konumuna gidebilirsiniz
- Yükleme spinner'ını gösteren ve gizleyen fonksiyonlar varken yalnızca gösterme çağrılarının kaldığı sorunu ararken,
-
Program davranışını çalışma anında değiştirme
- Koşullu breakpoint ifadesinde fonksiyon argümanlarının üzerine yazarsanız, tarayıcı içinde program davranışını anında değiştirebilirsiniz
- Örneğin
getPersonfonksiyonundakiiddeğerini değiştirebilirsiniz id=1gibi true olarak değerlendirilen ifadeler debugger'ı durdurur; durmamasını istiyorsanız sonuna, falseekleyebilirsiniz
-
Hızlı performans ölçümü
- Koşullu breakpoint değerlendirme süresi de araya girdiği için hassas performans profillemesi için uygun değildir
- Hızlı ölçüm gerektiğinde başlangıç noktası koşuluna
console.time('label'), bitiş noktası koşulunaconsole.timeEnd('label')koyarak çalışma süresini konsola yazdırabilirsiniz
Durma koşullarını daha hassas hale getirmek
-
Fonksiyon argüman sayısını kullanma
- Geçerli fonksiyon yalnızca 3 argümanla çağrıldığında durmak istiyorsanız şu koşulu kullanabilirsiniz
arguments.callee.length === 3- Opsiyonel argümanları olan overload edilmiş fonksiyonlar için faydalıdır
- Tanımlı argüman sayısı ile fiilen geçirilen argüman sayısı farklı olduğunda durmak istiyorsanız şu koşulu kullanabilirsiniz
(arguments.callee.length) != arguments.length- Fonksiyon çağrı konumundaki hataları bulmaya yardımcı olur
-
Zaman tabanlı koşullar
- Sayfa yüklendikten sonra 5 saniye geçmeden durmamasını istiyorsanız şu koşulu kullanabilirsiniz
performance.now() > 5000- Breakpoint ile yalnızca ilk sayfa yüklemesinden sonra ilgileniyorsanız uygundur
- Breakpoint ayarlandıktan sonraki ilk 5 saniye durmasın, sonrasında dursun istiyorsanız referans zamanı global değişkende tutabilirsiniz
window.baseline = window.baseline || Date.now(), (Date.now() - window.baseline) > 5000- Konsolda
window.baseline = Date.now()ile sayacı yeniden başlatabilirsiniz
-
CSS durumuna dayalı koşullar
- Hesaplanmış CSS değerlerini koşul olarak kullanıp durabilirsiniz
- Örneğin yalnızca
document.bodyarka plan rengi kırmızıysa durmak için şu koşulu kullanabilirsiniz
window.getComputedStyle(document.body).backgroundColor === "rgb(255,0,0)" -
Çalıştırma sayısı ve örnekleme
- Satır her çalıştığında sayacı artırarak iki çalıştırmada bir durabilirsiniz
window.counter = (window.counter || 0) + 1, window.counter % 2 === 0- Rastgele örnekleme ile 10 çalıştırmada 1 kez durmak istiyorsanız şu koşulu kullanabilirsiniz
Math.random() < 0.1 -
Belirli konumlarda asla durmamak
- Chrome'da gutter'a sağ tıklayıp “Never Pause Here” seçerseniz, her zaman
falseolan bir koşullu breakpoint oluşturulur ve o satırda durulmaz - XHR breakpoint'inde belirli satırları hariç tutarken veya belirli istisnaları yok sayarken kullanılabilir
- Chrome'da gutter'a sağ tıklayıp “Never Pause Here” seçerseniz, her zaman
-
Instance ID'lerini otomatik verme
- Sınıf constructor'ına koşullu breakpoint koyarak her instance'ı bir diziye kaydedebilirsiniz
(window.instances = window.instances || []).push(this)- Belirli bir instance'ın benzersiz ID'si dizi indeksiyle görülebilir
window.instances.indexOf(instance) -
Breakpoint'leri global olarak açıp kapatma
- Bir veya daha fazla koşullu breakpoint'i global boolean ile kontrol edebilirsiniz
- Bunu konsoldan doğrudan değiştirerek, başka bir breakpoint'ten değiştirerek veya timer ile değiştirerek yapabilirsiniz
window.enableBreakpoints = true setTimeout(() => (window.enableBreakpoints = true), 5000)
Fonksiyon ve sınıf çağrılarını izleme
-
Sınıf metodu çağrılarını izleme
- Chrome'un
monitorkomut satırı metodunu kullanarak sınıf metodu çağrılarını kolayca izleyebilirsiniz Dogsınıfının tüm instance'ları için metot çağrılarını görmek istiyorsanız, prototype üzerindeki tüm property'leremonitoruygulayabilirsiniz
var p = Dog.prototype; Object.getOwnPropertyNames(p).forEach((k) => monitor(p[k]));- Konsolda
function bark called with arguments: 2gibi çağrı bilgileri görünür - Yalnızca log bırakmak yerine çağrı anında yürütmeyi durdurmak istiyorsanız
monitoryerinedebugkullanabilirsiniz
- Chrome'un
-
Belirli bir instance üzerinden sınıfı izleme
- Sınıf adını bilmiyor ama bir instance'a sahipseniz, constructor'ın prototype'ı üzerinden aynı şekilde izleyebilirsiniz
var p = instance.constructor.prototype; Object.getOwnPropertyNames(p).forEach((k) => monitor(p[k]));- Belirli bir sınıfa sabitlenmeden, rastgele bir instance üzerinde çalışan fonksiyonlar yazarken faydalıdır
-
Konsoldan fonksiyon çağrısından hemen önce hata ayıklama
- Hata ayıklamak istediğiniz fonksiyonu konsoldan çağırmadan önce
debuggerçalıştırıp, sonraki fonksiyon çağrısına Step into yaparak fonksiyon uygulamasının içine girebilirsiniz
debugger; fn(1);- Fonksiyonun tanımlandığı yeri bulmakla uğraşmak istemediğinizde veya
fndinamik olarak bağlandığı için kaynak konumunu bilmediğinizde faydalıdır - Chrome'da
debug(fn)çağırarakfnher çağrıldığında fonksiyon içinde durmasını da sağlayabilirsiniz
- Hata ayıklamak istediğiniz fonksiyonu konsoldan çağırmadan önce
URL değişiklikleri ve özellik okuma hata ayıklaması
-
SPA yönlendirmesinden önce durmak
- Tek sayfalı uygulama URL'yi değiştirmeden hemen önce durmak istiyorsanız,
history.pushState,history.replaceState,window.onhashchange,window.onpopstateüzerinedebuggeriçeren fonksiyonlar bağlayabilirsiniz
const dbg = () => { debugger; }; history.pushState = dbg; history.replaceState = dbg; window.onhashchange = dbg; window.onpopstate = dbg;dbg'nin bu sürümü, gezinmeyi bozmadan durmayı sağlayan bir uygulama içermezwindow.location.replaceveyawindow.location.assigndoğrudan çağrıldığında, atamadan hemen sonra sayfa unload olacağı için bu yöntem işe yaramaz- Chrome'da ilgili metotlara
debugkoyarak yönlendirme kaynağını ve o andaki durumu görebilirsiniz
debug(window.location.replace); debug(window.location.assign); - Tek sayfalı uygulama URL'yi değiştirmeden hemen önce durmak istiyorsanız,
-
Özellik okuma noktasını bulma
- Bir nesnedeki belirli bir property'nin ne zaman okunduğunu bilmek istiyorsanız, getter içine
debuggerkoyabilirsiniz
{ get configOption() { debugger; return true; } }- Bir yapılandırma seçeneğinin hangi kod tarafından nasıl kullanıldığını görmek için faydalıdır
- Bunu kaynak kodda değiştirerek veya koşullu breakpoint ile uygulayabilirsiniz
- Bir nesnedeki belirli bir property'nin ne zaman okunduğunu bilmek istiyorsanız, getter içine
Konsoldan veri kopyalama
copy()konsol API'si ile metin kesilmeden tarayıcı bilgilerini panoya kopyalayabilirsiniz- Geçerli DOM snapshot'ını, görsel gibi kaynakların metadata'sını, büyük JSON blob'larını ve localStorage dökümlerini kopyalarken kullanılabilir
copy(document.documentElement.outerHTML)
copy(performance.getEntriesByType("resource"))
copy(JSON.parse(blob))
copy(localStorage)
HTML/CSS hata ayıklama
-
JS yürütmesi durmuşken DOM inceleme
- DOM inspector'da Chrome/Windows için
ctrl+\tuşlarına basarak istediğiniz anda JS yürütmesini durdurabilirsiniz - JS'in DOM'u değiştirdiği veya mouseover gibi event'lerin DOM'u değiştirdiği durumlarda bile geçerli DOM snapshot'ını kararlı biçimde inceleyebilirsiniz
- DOM inspector'da Chrome/Windows için
-
Kaybolan öğeleri inceleme
- Fareyi hareket ettirince kaybolan koşullu DOM öğelerini, konsolda 5 saniyelik timer kurup debugger çalıştırarak inceleyebilirsiniz
setTimeout(function() { debugger; }, 5000);- 5 saniye boyunca UI'yi görünür hale getirdikten sonra JS yürütmesi durursa öğe kaybolmayacağı için geliştirici araçlarına geçip inceleyebilirsiniz
- JS yürütmesi durmuşken öğe inceleme, CSS düzenleme ve JS konsol komutları çalıştırma mümkündür
- İmleç konumu veya focus'a bağlı DOM'u incelerken etkilidir
-
DOM snapshot kaydetme
- Geçerli DOM'u kopyalamak için şu komutu kullanabilirsiniz
copy(document.documentElement.outerHTML);- Her 1 saniyede bir DOM string'ini bir diziye kaydetmek için
setInterval'i şöyle kullanabilirsiniz
doms = []; setInterval(() => { const domStr = document.documentElement.outerHTML; doms.push(domStr); }, 1000);- Diziye kaydetmeden konsola sürekli yazdırmanız da mümkündür
-
Odaklanan öğeyi izleme
document.activeElementdeğerini periyodik olarak karşılaştırarak focus değişikliklerini konsola yazdırabilirsiniz
(function () { let last = document.activeElement; setInterval(() => { if (document.activeElement !== last) { last = document.activeElement; console.log("Focus changed to: ", last); } }, 100); })(); -
Kalın gösterilen öğeleri bulma
- Tüm öğelerde hesaplanmış
fontWeightdeğeri"bold"veya"700"olanları bulabilirsiniz
const isBold = (e) => { let w = window.getComputedStyle(e).fontWeight; return w === "bold" || w === "700"; }; Array.from(document.querySelectorAll("*")).filter(isBold);- Geçerli inspector'da seçili öğenin yalnızca alt öğelerini bulmak istiyorsanız
$0üzerinden arama yapabilirsiniz
Array.from($0.querySelectorAll("*")).filter(isBold); - Tüm öğelerde hesaplanmış
-
Seçili öğe ve event'lerle çalışma
- Konsoldaki
$0, Element Inspector'da şu anda seçili olan öğeye işaret eden otomatik referanstır - Chrome ve Edge'de daha önce incelenen öğelere
$1, ondan öncekine$2şeklinde erişebilirsiniz - Chrome'da seçili öğenin event listener'larını görebilirsiniz
getEventListeners($0)- Seçili öğedeki tüm event'leri hata ayıklamak için
monitorEvents($0)kullanabilirsiniz - Yalnızca belirli event gruplarını görmek istiyorsanız bir dizi verebilirsiniz
monitorEvents($0, ["control", "key"]) - Konsoldaki
1 yorum
Hacker News yorumları
Son birkaç on yılda tarayıcılara yerleşik gelen hata ayıklama araçları gerçekten çok gelişti.
Uzun süredir JS kullanan biri olarak, tarayıcı içinde kod hata ayıklamayı bu kadar sezgisel hâle getiren insanlara derinden minnettarım.
Backend veya başka diller gibi diğer geliştirme alanlarına geçtiğimde, modern tarayıcıların varsayılan olarak sunduğu bu hata ayıklama aracı ekosistemini özlüyorum.
Python, Java, C++ ile yıllarca çok hata ayıkladım ama araçların yetersiz olduğunu hiç hissetmedim.
Elbette bu alanda kullanılabilecek seçenekleri bilmeyen epey kişi gördüm.
O cehennemdense konsolu seçerim.
setTimeout(function() { debugger; }, 5000);akıllıca.Sonuçta Chrome debugger’ın kendi kendini debug ettiği özyinelemeli kaplumbağa yığınını yenmenin tek yolu
debuggerifadesi.Kötü şöhretli Myspace Sammy solucanının sam.pl’si de ziyaretçilerin obfuscate edilmiş HTML ana sayfasını kurcalamasını engellemek için hata ayıklama tuzakları kullanmıştı.
Ana döngüyü çalıştırıp oraya buraya can sıkıcı
debugger;ifadeleri yerleştirir ve bunları 30 FPS / 32 ms aralıklarla çalıştırarak DevTools’u işe yaramaz hâle getirir.Çünkü
debuggerifadesini “yok saymanın” bir yolu yoktur.cmd-\kullanabilirsiniz.Sayfa o anda çalışan JS satırında durur ve sorunlu öğeyi her zamanki gibi inceleyebilirsiniz.
debuggerifadelerini yok saymasını isterdim.Meşru
debuggerifadesi kullanım örneklerinin çoğu, kendi kodunuz üzerinde çalışıyorsanız basit breakpoint’lerle değiştirilebilir.queryObjects’ın eksik olması göze çarpıyor.Belirli bir constructor ile oluşturulmuş tüm nesnelerin listesini döndüren epey çılgın bir API.
Örneğin
queryObjects(Function)ile heap’teki tüm fonksiyonların listesini alabilirsiniz.Bir modülün içindeki “private” fonksiyonları bile döndürür.
Gerçekten çılgın bir özellik ve muhtemelen Chromium’a özel.
Watch değişkenlerini bir türlü düzgün kullanamıyorum.
Scope ve yenileme kuralları fazlasıyla anlaşılmaz; sadece global değişkenler izlenebiliyor sanıyorsunuz ama o bile beklendiği gibi çalışmıyor, bu yüzden test ederken sonunda log’ları değerlerle dolduruyorum.
Yıllardır, değişkenleri ve ayar değerlerini görüp test edebilmek için konsola Data.gui [1] tarzı bir UI konması gerektiğini düşünüyorum.
Bu CodePen’de [2] gerçekten çalıştığını görebilirsiniz.
Tarayıcılarda teoride harika birçok hata ayıklama özelliği var ama pratikte güvenilir şekilde çalışıyor gibi görünmüyorlar.
Breakpoint’lerin bile hepsinin güvenilir biçimde tutmasını sağlayamıyorum.
Kod açılmış hâldeyken sorun yok, ama bundle edilir edilmez, minify edilmemiş olsa bile birçok debugger özelliği bozuluyor gibi geldi.
Frontend mühendisi olmadığım için belki de bir şeyi yanlış yapıyorumdur.
Hayal kırıklığının nedeni büyük ihtimalle bu; açıkken aktif hata ayıklama deneyimi bu kadar kötüyken neden hâlâ varsayılan olarak açık olduğunu anlamıyorum.
IIFE kapsamı içinde kodun çalışmasını durdurmadan IIFE’nin yerel değişkenlerine erişmenin bir yolu olsa iyi olurdu
Debugger’ı bunu yapmaya ikna etmenin bir yolu var mı?
https://en.m.wikipedia.org/wiki/Immediately_invoked_function...
IIFE’nin tamamen dışındaysanız bunun imkânsız olduğunu düşünüyorum
Aradığınız değişken, çalışmadan önce veya sonra bellekte bulunmayabilir
JavaScript’te diğer dillerdeki gibi statik değişkenler yok; bu yüzden IIFE her çağrıldığında iç değişkenler atılır
JavaScript, web worker gibi şeyler kullanılmadığı sürece oldukça güçlü bir tek iş parçacıklı modele sahiptir; bu nedenle
async/awaitçağrılarıyla doğrudan bir yarış durumu yaratmaya çalışmıyorsanız, IIFE kapsamı dışında böyle bir değişkenin bellekte kalması kavramı pek geçerli olmazvar’ı kötüye kullanıp değişkeni IIFE kapsamı dışında ilklendirirseniz, son IIFE çağrısının oluşturduğu değeri görebilirsinizBiraz değişiklik gerekir ama harcanan emeğe göre getirisi yüksek
https://github.com/kristopolous/_inject
İstediğiniz bir anda neredeyse her fonksiyon bağlamında dolaşıp ne olduğunu görebilirsiniz
Bağlamın yok edilmesini engellemek için referans sayacını kötüye kullanan bir yöntem
İstemci tarafı JS ile çok uğraştığım dönemde gerçekten kullanışlıydı
Bunun düzgün yapılmış killer app sürümü, herhangi bir bağlamda REPL açmak olurdu
Mevcut hâliyle bile iyi kullanmak için epey uzmanlık gerekiyor
https://firefox-source-docs.mozilla.org/devtools-user/debugg...
Düzenleme: Bağlantılı yazıda kelimenin tam anlamıyla ilk ele alınan şeyin bu olduğunu şimdi fark ettim
[[Scopes]]sözde özelliğine bakabilirsinizFirefox’ta bir yolu yok gibi görünüyor
Tamlık adına Werkzeug önerilebilir
Django backend geliştirmede kullanıyorum ve inanılmaz faydalı
İstisna oluşan herhangi bir yerde, istediğiniz zaman tarayıcı içinde “PDB” kabuğu kullanmanızı sağlıyor
1/0koymanız yeterli, kolayÇok basit ve sezgisel
django-extension/runserver_plusgerekiyor mu?Sürekli kullandığım bir numara, yüklenen script’leri UI metinleriyle arayıp debug etmektir
Network paneline gidip ağ isteği kaydını başlatın, sol kenar çubuğunu açın ve bulmak istediğiniz kodu veya UI metnini arayın
Genellikle tuhaf bir paketlenmiş JS chunk dosyasının içinde bulunur; sonuca tıklayınca o dosyanın ağ isteği açılır
Dosyanın içinde herhangi bir yere sağ tıklayıp “Open in Sources” benzeri bir öğeyi seçerseniz debugger’a geçersiniz
Artık
debuggerifadesi koyabilirsiniz; bu süreçte source map’ler de muhtemelen yüklenirUzun süre Python/Elixir programcısı olarak çalıştım ve
pdb.set_trace()ileIEx.pry()’ı çok kullandımYakın zamanda çok dağınık bir NodeJS backend devraldım ve düzgün debug araçları olmadan kalan saçlarımı yoluyorum
console.logile debug etmeye geri döndüm ama kendimi ilkel insan gibi hissediyorumBu kadar popüler bir ekosistemde düzgün bir debug REPL’i olmadığına inanamıyorum; nereye bakmam gerektiğini söyleyebilir misiniz?
node --inspect-brkkullanabilirsiniz; VS Code veya Chrome DevTools ile bağlanabilirsinizZor kısım, TypeScript dönüşümü gibi build araçlarının
nodejskomutunun önünde olup olmadığıSadece
nodeçalıştırıyorsanız oldukça kolayDebug REPL’i JavaScript konsolunun kendisidir
Debug seçeneklerine eklerseniz breakpoint koyup fonksiyonların içine adım adım girebilirsiniz
Çoğu şey nesnelerin içinde kapsüllendiği için JavaScript debug etmek için oldukça iyi
--inspectveya--inspect-brkVSCode’da “open JavaScript debug terminal” komutunu da kullanabilirsiniz
Bu komut, herhangi bir
nodekomutunun otomatik olarak debugger bağlı şekilde başladığı bir terminal açarhttps://pypi.org/project/snoop/
Derin izleme için bir decorator
Node JS ekosisteminde böyle bir özellik görmedim
“Debugging Property Reads” bölümünde, koşullu breakpoint kullanarak
{configOption: true}ifadesini{get configOption() { debugger; return true; }}biçimine nasıl dönüştürdüğünü merak ediyorumKonsolda değerin üzerine bir getter yazabilirsiniz; yazma izniniz varsa kaynak kodda doğrudan da değiştirebilirsiniz
Bunu kesin öğrenmem gerekiyor gibi geliyor; acaba buna özel bir kitap ya da öğrenme kaynağı var mı?
Yoksa web geliştirme ve frontend’i durmadan derinlemesine kurcalamaktan başka çare yok mu?
Yoğun çalışırken sürüm notlarını okumak için bir bağlantıya tıklamak pek cazip gelmeyebilir, ama yeni bir sürüm çıktığında 5 dakika göz atmak bile kesinlikle değer.
Birkaç dakikalık, sindirmesi kolay videolar da var; hızlı bir genel bakış edinmek için iyi oluyor.
Amaç yeni özellikleri tanıtmak olsa da, deneyimime göre aracın mevcut sınırlarını anlamayı da sık sık sağlıyor.