ब्राउज़र में इस्तेमाल होने वाली डिबगिंग तकनीकें
(alan.norbauer.com)- अगर आप ब्राउज़र Developer Tools को मध्यम स्तर से ऊपर तक इस्तेमाल कर सकते हैं, तो conditional breakpoints को सिर्फ stop condition नहीं बल्कि log, trace, state change और time measurement tools तक विस्तार दे सकते हैं
- अगर
console.log,console.count,console.trace,console.timeजैसे console API को breakpoint या Watch Pane में रखें, तो execution रोके बिना flow और state को रिकॉर्ड किया जा सकता है - function argument count, time, CSS computed values, execution count, random sampling और global boolean को मिलाकर कब रुकना है इसे कहीं ज़्यादा सटीक ढंग से नियंत्रित किया जा सकता है
- Chrome के
monitor,debug,copy,$0,getEventListeners,monitorEventsसे function calls, DOM, events और clipboard operations को console से तेज़ी से संभाला जा सकता है - जिन स्क्रीन पर DOM, JS या user events की वजह से लगातार बदलता रहता है, वहाँ JS pause,
setTimeout(debugger), DOM snapshots और focus monitoring HTML/CSS debugging में खास तौर पर उपयोगी हैं
conditional breakpoints को debugging tool के रूप में बढ़ाना
-
conditional breakpoint में सिर्फ true/false condition ही नहीं, बल्कि side effects वाले JavaScript expressions भी डाले जा सकते हैं
-
इसका उपयोग करके basic breakpoint को log, trace, state change और time measurement tool की तरह इस्तेमाल किया जा सकता है
-
logpoint और tracepoint
- conditional breakpoint में
console.logकॉल करने पर वह execution रोके बिना console में value छोड़ने वाले logpoint की तरह काम करता है console.countइस्तेमाल करने पर यह भी गिना जा सकता है कि वह line कितनी बार execute हुई- मई 2020 के update के अनुसार प्रमुख browsers logpoint/tracepoint को सीधे support करते हैं
- conditional breakpoint में
-
Watch Pane में console API का उपयोग
- Watch Pane में भी
console.logजैसे expressions डाले जा सकते हैं - application जब भी debugger में रुकता है, तब
console.table(localStorage)से localStorage snapshot लिया जा सकता है - DOM change breakpoint और Watch expressions को साथ में इस्तेमाल करने पर DOM subtree change के बाद snapshot को array में store किया जा सकता है
(window.doms = window.doms || []).push(document.documentElement.outerHTML)- DOM change breakpoint को execution रोके बिना काम करने के लिए सेट नहीं किया जा सकता
- Watch Pane में भी
-
call stack trace करना
- जब loading spinner दिखाने वाली function और छिपाने वाली function हों, और समस्या यह हो कि सिर्फ show call बच रहा हो, तब
console.traceको conditional breakpoint में डाल सकते हैं - show function की आख़िरी stack trace में caller पर click करके उस call location पर जा सकते हैं
- जब loading spinner दिखाने वाली function और छिपाने वाली function हों, और समस्या यह हो कि सिर्फ show call बच रहा हो, तब
-
runtime में program behavior बदलना
- conditional breakpoint expression में function arguments को overwrite करके ब्राउज़र के अंदर program behavior को तुरंत बदला जा सकता है
- उदाहरण के लिए
getPersonfunction केidvalue को बदला जा सकता है id=1जैसे truthy expression debugger को रोक देंगे, इसलिए अगर रुकना नहीं हो तो, falseजोड़ सकते हैं
-
तेज़ performance measurement
- क्योंकि conditional breakpoint evaluation time भी इसमें मिल जाता है, इसलिए यह precise performance profiling के लिए उपयुक्त नहीं है
- जब तेज़ measurement चाहिए, तब start point condition में
console.time('label')और end point condition मेंconsole.timeEnd('label')डालकर execution time console में छोड़ा जा सकता है
break condition को और सटीक बनाना
-
function argument count का उपयोग
- अगर current function सिर्फ 3 arguments के साथ call होने पर ही रुकना चाहिए, तो यह condition इस्तेमाल की जा सकती है
arguments.callee.length === 3- optional arguments वाले overloaded function में यह उपयोगी है
- अगर defined argument count और actual passed argument count अलग हों तभी रुकना हो, तो यह condition इस्तेमाल की जा सकती है
(arguments.callee.length) != arguments.length- यह function call location की bug ढूँढने में मदद करता है
-
time-based conditions
- अगर page load के 5 सेकंड बाद ही रुकना हो, उससे पहले नहीं, तो यह condition इस्तेमाल की जा सकती है
performance.now() > 5000- जब breakpoint सिर्फ initial page load के बाद ही महत्वपूर्ण हो, तब यह उपयुक्त है
- अगर breakpoint सेट होने के बाद अगले 5 सेकंड तक न रुके और उसके बाद रुकने लगे, तो baseline time को global variable में store किया जा सकता है
window.baseline = window.baseline || Date.now(), (Date.now() - window.baseline) > 5000- console में
window.baseline = Date.now()चलाकर counter को फिर से शुरू किया जा सकता है
-
CSS state-based conditions
- computed CSS values को भी condition बनाकर रोका जा सकता है
- उदाहरण के लिए, अगर
document.bodyका background color लाल होने पर ही रुकना हो, तो यह condition इस्तेमाल की जा सकती है
window.getComputedStyle(document.body).backgroundColor === "rgb(255,0,0)" -
execution count और sampling
- line हर बार execute होने पर counter बढ़ाकर हर दूसरी बार रुकना संभव है
window.counter = (window.counter || 0) + 1, window.counter % 2 === 0- random sampling के लिए 10 में से 1 बार ही रुकना हो, तो यह condition इस्तेमाल की जा सकती है
Math.random() < 0.1 -
कुछ locations पर कभी न रुकना
- Chrome में gutter पर right-click करके “Never Pause Here” चुनने पर हमेशा
falseरहने वाला conditional breakpoint बनता है, जिससे उस line पर रुकना बंद हो जाता है - इसे XHR breakpoint में किसी खास line को exclude करने या कुछ exceptions को ignore करने के लिए इस्तेमाल किया जा सकता है
- Chrome में gutter पर right-click करके “Never Pause Here” चुनने पर हमेशा
-
instance ID अपने-आप देना
- class constructor में conditional breakpoint डालकर हर instance को array में store किया जा सकता है
(window.instances = window.instances || []).push(this)- किसी specific instance की unique ID को array index से जाना जा सकता है
window.instances.indexOf(instance) -
breakpoint global toggle
- एक या एक से ज़्यादा conditional breakpoints को global boolean से control किया जा सकता है
- इसे console से सीधे, किसी दूसरे breakpoint से, या timer के ज़रिए बदला जा सकता है
window.enableBreakpoints = true setTimeout(() => (window.enableBreakpoints = true), 5000)
function और class calls का trace करना
-
class method calls की निगरानी
- Chrome के
monitorcommand-line method का उपयोग करके class method calls को आसानी से trace किया जा सकता है - अगर
Dogclass के सभी instances के method calls देखना हो, तो prototype की सभी properties परmonitorapply किया जा सकता है
var p = Dog.prototype; Object.getOwnPropertyNames(p).forEach((k) => monitor(p[k]));- console में
function bark called with arguments: 2जैसी call information दिखाई देगी - अगर सिर्फ log छोड़ने के बजाय call पर execution रोकना हो, तो
monitorकी जगहdebugइस्तेमाल किया जा सकता है
- Chrome के
-
किसी specific instance से class trace करना
- अगर class name नहीं पता लेकिन instance मौजूद है, तो constructor के prototype के ज़रिए इसी तरह monitoring की जा सकती है
var p = instance.constructor.prototype; Object.getOwnPropertyNames(p).forEach((k) => monitor(p[k]));- यह किसी specific class तक सीमित हुए बिना arbitrary instance पर काम करने वाले function बनाने में उपयोगी है
-
console से function call से ठीक पहले debugging
- जिस function को debug करना है, उसे console से call करने से पहले
debuggerचलाएँ, फिर अगले function call पर Step into करें, तो function implementation के अंदर जा सकते हैं
debugger; fn(1);- जब function definition location ढूँढना झंझट भरा हो, या
fndynamically bind हुआ हो और source location पता न हो, तब यह उपयोगी है - Chrome में
debug(fn)कॉल करके भीfnके हर call पर function के अंदर रुकवाया जा सकता है
- जिस function को debug करना है, उसे console से call करने से पहले
URL change और property read debugging
-
SPA routing से पहले रुकना
- अगर single-page application के URL बदलने से ठीक पहले रुकना हो, तो
history.pushState,history.replaceState,window.onhashchange,window.onpopstateपरdebuggerवाली function जोड़ सकते हैं
const dbg = () => { debugger; }; history.pushState = dbg; history.replaceState = dbg; window.onhashchange = dbg; window.onpopstate = dbg;dbgका यह version navigation को तोड़े बिना pause करने वाला implementation शामिल नहीं करता- अगर
window.location.replaceयाwindow.location.assignको सीधे call किया जाता है, तो assignment के तुरंत बाद page unload हो जाता है, इसलिए यह तरीका वहाँ काम नहीं करेगा - Chrome में संबंधित methods पर
debugलगाकर redirect source और उस समय की state देखी जा सकती है
debug(window.location.replace); debug(window.location.assign); - अगर single-page application के URL बदलने से ठीक पहले रुकना हो, तो
-
property read होने की जगह ढूँढना
- अगर जानना हो कि object की किसी specific property को कब read किया जा रहा है, तो getter के अंदर
debuggerरखा जा सकता है
{ get configOption() { debugger; return true; } }- यह देखने में उपयोगी है कि कोई code configuration option को कैसे और कहाँ इस्तेमाल करता है
- इसे original source में बदलकर या conditional breakpoint के ज़रिए लागू किया जा सकता है
- अगर जानना हो कि object की किसी specific property को कब read किया जा रहा है, तो getter के अंदर
console से data copy करना
copy()console API का उपयोग करके browser की जानकारी को string truncation के बिना clipboard में कॉपी किया जा सकता है- current DOM snapshot, image जैसी resources की metadata, बड़े JSON blob, और localStorage dump कॉपी करने में यह उपयोगी है
copy(document.documentElement.outerHTML)
copy(performance.getEntriesByType("resource"))
copy(JSON.parse(blob))
copy(localStorage)
HTML/CSS debugging
-
JS execution रुकी हुई अवस्था में DOM inspect करना
- DOM inspector में Chrome/Windows के आधार पर
ctrl+\दबाकर कभी भी JS execution रोकी जा सकती है - जब JS DOM बदल रहा हो, या mouseover जैसे events DOM बदलते हों, तब भी current DOM snapshot को स्थिर रूप से inspect किया जा सकता है
- DOM inspector में Chrome/Windows के आधार पर
-
गायब हो जाने वाले elements inspect करना
- mouse हिलाने पर गायब हो जाने वाले conditional DOM elements को inspect करने के लिए console में 5 सेकंड का timer लगाकर debugger चलाया जा सकता है
setTimeout(function() { debugger; }, 5000);- 5 सेकंड तक UI खोलने के बाद जब JS execution रुकती है, तो element गायब नहीं होगा, इसलिए Developer Tools में जाकर उसे inspect किया जा सकता है
- JS execution रुकी रहने के दौरान element inspection, CSS editing और JS console commands चलाना संभव है
- यह cursor position या focus पर निर्भर DOM को inspect करने में प्रभावी है
-
DOM snapshots रिकॉर्ड करना
- current DOM कॉपी करने के लिए यह command इस्तेमाल की जा सकती है
copy(document.documentElement.outerHTML);- अगर हर 1 सेकंड पर DOM string को array में store करना हो, तो
setIntervalइस तरह इस्तेमाल किया जा सकता है
doms = []; setInterval(() => { const domStr = document.documentElement.outerHTML; doms.push(domStr); }, 1000);- array में store किए बिना इसे लगातार console में print भी किया जा सकता है
-
focused element की निगरानी
document.activeElementको समय-समय पर compare करके focus change को console में print किया जा सकता है
(function () { let last = document.activeElement; setInterval(() => { if (document.activeElement !== last) { last = document.activeElement; console.log("Focus changed to: ", last); } }, 100); })(); -
bold दिखने वाले elements ढूँढना
- सभी elements में computed
fontWeightका मान"bold"या"700"होने वाले elements ढूँढे जा सकते हैं
const isBold = (e) => { let w = window.getComputedStyle(e).fontWeight; return w === "bold" || w === "700"; }; Array.from(document.querySelectorAll("*")).filter(isBold);- अगर अभी inspector में चुने गए element के descendants में ही ढूँढना हो, तो
$0के आधार पर search किया जा सकता है
Array.from($0.querySelectorAll("*")).filter(isBold); - सभी elements में computed
-
चुने गए element और events को संभालना
- console का
$0, Element Inspector में currently selected element का auto-reference है - Chrome और Edge में पहले inspect किए गए elements को
$1, और उससे पहले वाले को$2की तरह access किया जा सकता है - Chrome में currently selected element के event listeners देखे जा सकते हैं
getEventListeners($0)- selected element के सभी events को debug करने के लिए
monitorEvents($0)इस्तेमाल किया जा सकता है - अगर सिर्फ specific event groups देखने हों, तो array pass किया जा सकता है
monitorEvents($0, ["control", "key"]) - console का
1 टिप्पणियां
Hacker News की राय
पिछले कई दशकों में ब्राउज़र में बने हुए debugging tools सच में बहुत आगे बढ़े हैं
JS को लंबे समय से इस्तेमाल करने वाले के नाते, मैं उन लोगों का गहराई से आभारी हूँ जिन्होंने ब्राउज़र के अंदर code debugging को इतना सहज बना दिया
जब backend या दूसरी languages जैसे अन्य development क्षेत्रों में जाते हैं, तो modern browser जो debugging tools ecosystem by default देता है उसकी कमी खलती है
मैंने Python, Java, C++ में भी कई वर्षों तक खूब debugging की है, लेकिन tools की कमी कभी महसूस नहीं हुई
हाँ, इस क्षेत्र में उपलब्ध विकल्पों को न जानने वाले लोग काफी देखे हैं
उस नरक से तो मैं console चुनूँगा
setTimeout(function() { debugger; }, 5000);काफ़ी चतुर हैआखिरकार Chrome debugger के खुद को ही debug करने वाले recursive turtle stack पर जीत पाने का एकमात्र तरीका
debuggerstatement ही हैबदनाम Myspace Sammy worm के sam.pl ने भी visitors को obfuscated HTML homepage खंगालने से रोकने के लिए debugging trap का इस्तेमाल किया था
वह main loop चलाती है, इधर-उधर परेशान करने वाले
debugger;statements डालती है, और 30FPS / 32ms interval पर execute करती है ताकि DevTools बेकार हो जाएक्योंकि
debuggerstatement को “ignore” करने का कोई तरीका नहीं हैcmd-\\इस्तेमाल किया जा सकता हैpage उसी JS line पर रुक जाएगा जो उस समय चल रही थी, और फिर आप सामान्य तरह से problematic element inspect कर सकते हैं
debuggerstatement को ignore कर देतेdebuggerstatement के ज्यादातर वैध उपयोग मामलों को, अगर आप अपने ही code पर काम कर रहे हैं, साधारण breakpoint से बदला जा सकता हैयह देखकर हैरानी होती है कि
queryObjectsगायब हैयह एक काफ़ी पागलपन भरा API है जो किसी खास constructor से बने सभी objects की सूची लौटा देता है
उदाहरण के लिए
queryObjects(Function)से heap में मौजूद सभी functions की सूची मिल सकती हैयह किसी module के अंदर की “private” functions तक वापस दे देता है
यह वाकई पागलपन वाली सुविधा है, और शायद Chromium-only होगी
मैं watch variables को ठीक से कभी इस्तेमाल ही नहीं कर पाता
scope और update rules इतने अस्पष्ट हैं कि लगता है शायद सिर्फ global variables ही watch किए जा सकते हैं, लेकिन वह भी उम्मीद के मुताबिक काम नहीं करते, इसलिए test करते समय आखिर में values के logs से console भरना पड़ता है
मैं कई सालों से सोच रहा हूँ कि console में Data.gui [1] स्टाइल का UI होना चाहिए ताकि variables और settings values को देखते हुए test किया जा सके
इस CodePen [2] में इसे सचमुच काम करते देखा जा सकता है
browser में सिद्धांत रूप से बेहतरीन debugging features बहुत हैं, लेकिन व्यवहार में वे भरोसेमंद ढंग से काम करते नहीं लगते
breakpoints तक को पूरी तरह स्थिर रूप से लगवाना मुश्किल होता है
code unbundled हो तो ठीक लगता है, लेकिन bundling होते ही, भले minify न किया गया हो, debugger की बहुत-सी सुविधाएँ टूटती हुई लगीं
मैं frontend engineer नहीं हूँ, इसलिए हो सकता है मैं ही कुछ गलत कर रहा हूँ
शायद वही इस झुंझलाहट की वजह हों, और active debugging के दौरान अनुभव इतना खराब होने के बावजूद वे default में on क्यों हैं, यह आज तक समझ नहीं आया
अगर कोड का execution रोके बिना IIFE के local variables तक पहुँचने का कोई तरीका हो तो अच्छा होगा
क्या debugger को मनाकर ऐसा करवाने का कोई तरीका है?
https://en.m.wikipedia.org/wiki/Immediately_invoked_function...
अगर आप पूरी तरह IIFE के बाहर हैं, तो यह संभव नहीं लगता
जिस variable को आप ढूँढ रहे हैं, वह execution से पहले या बाद में memory में मौजूद भी न हो सकता है
JavaScript में दूसरी भाषाओं की तरह static variables नहीं होते, इसलिए हर बार IIFE कॉल होने पर उसके अंदर के variables हटा दिए जाते हैं
JavaScript का single-threaded मॉडल काफ़ी मज़बूत है, जब तक आप web worker जैसी चीज़ें नहीं इस्तेमाल कर रहे; इसलिए अगर आप
async/awaitcalls से जानबूझकर race condition नहीं बना रहे, तो IIFE scope के बाहर ऐसे variables के memory में बचे रहने की अवधारणा ही मुश्किल से लागू होती हैअगर आप
varका ज़्यादा इस्तेमाल करके IIFE scope के बाहर variables initialize करते हैं, तो आख़िरी IIFE call द्वारा बनाया गया value देख सकते हैंउसमें थोड़ा modification चाहिए होगा, लेकिन मेहनत के हिसाब से फ़ायदा बड़ा है
https://github.com/kristopolous/_inject
किसी भी मनचाहे समय पर लगभग किसी भी function context में घूमकर देख सकते हैं कि क्या हुआ था
यह reference counter का दुरुपयोग करके context को नष्ट होने से बचाता है
जब मैं client-side JS बहुत करता था, तब यह सच में बहुत उपयोगी था
इसका ठीक से बनाया गया killer app version ऐसा होगा जो किसी भी context में REPL खोल दे
अभी की हालत में भी इसे अच्छी तरह इस्तेमाल करने के लिए काफ़ी skill चाहिए
https://firefox-source-docs.mozilla.org/devtools-user/debugg...
संपादन: अभी समझ आया कि linked post में literally सबसे पहले इसी की बात की गई है
[[Scopes]]pseudo-property देखेंFirefox में इसका कोई तरीका नहीं लगता
completeness के लिए Werkzeug की सिफारिश की जा सकती है
मैं इसे Django backend development में इस्तेमाल कर रहा हूँ और यह बेहद उपयोगी है
जहाँ भी exception होता है, वहाँ कभी भी browser के अंदर “PDB” shell इस्तेमाल करने देता है
1/0डालना होता है, इसलिए आसान हैयह बहुत simple और intuitive है
django-extension/runserver_plusकी ज़रूरत है?एक तरकीब जो मैं हमेशा इस्तेमाल करता हूँ, वह है UI strings से loaded scripts को search करके debugging करना
Network panel में जाएँ, network requests की recording शुरू करें, फिर left sidebar खोलकर वह code या UI string खोजें जिसे आप ढूँढ रहे हैं
आमतौर पर यह किसी अजीब bundle JS chunk file के अंदर मिलती है, और result पर क्लिक करने से उस file की network request खुल जाती है
उस file में कहीं भी right-click करके “Open in Sources” जैसा विकल्प चुनें, तो debugger पर पहुँच जाएँगे
अब
debuggerstatement रख सकते हैं, और इस process में source maps भी शायद load हो जाएँगेलंबे समय तक Python/Elixir programmer के रूप में काम करते हुए मैंने
pdb.set_trace()औरIEx.pry()का बहुत इस्तेमाल किया हैहाल ही में मुझे एक बेहद गंदा NodeJS backend संभालना पड़ा है, और बिना किसी ढंग के debugging tools के मैं अपने बचे-खुचे बाल नोच रहा हूँ
मैं फिर से
console.logdebugging पर लौट आया हूँ, लेकिन खुद को आदिमानव जैसा महसूस कर रहा हूँयक़ीन नहीं होता कि इतने लोकप्रिय ecosystem में कोई ढंग का debugging REPL नहीं है; क्या कोई बता सकता है कि मुझे कहाँ जाना चाहिए?
node --inspect-brkइस्तेमाल करें, और VS Code या Chrome DevTools से connect करेंमुश्किल हिस्सा यह है कि TypeScript transformation जैसे build tools
nodejscommand से पहले हैं या नहींअगर आप सीधे
nodeचला रहे हैं, तो यह काफ़ी आसान हैdebugging REPL असल में JavaScript console ही है
debugging options में जोड़ें, फिर breakpoint लगा सकते हैं और functions के अंदर step कर सकते हैं
ज़्यादातर चीज़ें objects के अंदर encapsulated होती हैं, इसलिए JavaScript debugging के लिए काफ़ी अच्छी है
--inspectया--inspect-brkचाहिएVSCode में “open JavaScript debug terminal” command भी इस्तेमाल कर सकते हैं
यह command ऐसा terminal खोलती है जिसमें कोई भी
nodecommand अपने-आप debugger attached होकर शुरू होता हैhttps://pypi.org/project/snoop/
यह deep tracing के लिए decorator है
Node JS ecosystem में मैंने ऐसी capability नहीं देखी है
“Debugging Property Reads” सेक्शन में मुझे यह जानना है कि conditional breakpoint का इस्तेमाल करके
{configOption: true}को{get configOption() { debugger; return true; }}में कैसे बदला जाता हैconsole में value को getter से override कर सकते हैं, और अगर write permission हो तो source code में सीधे भी बदल सकते हैं
यह ऐसी चीज़ लगती है जो ज़रूर सीखनी चाहिए, लेकिन सोच रहा हूँ कि क्या इसके लिए कोई खास किताब या सीखने की सामग्री है
या फिर बस वेब डेवलपमेंट और फ्रंटएंड में लगातार और गहराई तक जाते रहने के अलावा कोई रास्ता नहीं है?
जब आप किसी काम के बीच में हों, तो release notes पढ़ने के लिए लिंक पर क्लिक करने का मन शायद न करे, लेकिन नया version आने पर सिर्फ 5 मिनट भी सरसरी नज़र डालना पूरी तरह काबिल-ए-गौर है
कुछ ही मिनटों के आसान वीडियो भी हैं, जिनसे जल्दी एक साधारण overview मिल जाता है
मकसद भले नए फीचर्स का परिचय देना हो, लेकिन मेरे अनुभव में इससे अक्सर टूल की मौजूदा सीमाएँ भी समझ में आती हैं