Как дебажить node js
Перейти к содержимому

Как дебажить node js

  • автор:

Отладка¶

Под отладкой понимается процесс поиска и устранения возникающих в процессе работы приложения ошибок с помощью специально созданных для этого инструментов.

Для отладки Node.js приложений при запуске главного скрипта необходимо указать команду inspect , которая запускает отдельный процесс для получения и обработки сигналов отладки.

node inspect app.js 

По умолчанию процесс Node.js отладки запускается по адресу 127.0.0.1:9229 . Для задания других хоста и порта, используйте параметр —inspect в следующем формате.

node inspect --inspect=127.0.0.1:5000 app.js 

Для запуска приложения в режиме отладки и установки точки останова на первой же строке, вместо параметра —inspect используйте —inspect-brk .

Отладка Node js через CLI¶

Теперь рассмотрим процесс Node.js отладки на примере.

 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
const express = require('express'), app = express(); const host = '127.0.0.1'; const port = 7000; app.get('/', (req, res) =>  let counter = 0; counter += 5; debugger; res.send('Node js inspector'); >); app.listen(port, host, () => console.log(`Server listens http://$host>:$port>`) ); 

После запуска приведенного примера командой inspect , отладчик остановится на первой строке и предложит ввести команду. Список возможных команд:

  • cont — продолжает выполнение скрипта;
  • next — следующее действие (переходит на следующую строку кода);
  • step — «входит» в исходный код текущего исполняемого выражения;
  • step — «входит» в исходный код текущего исполняемого выражения;
  • pause — ставит на паузу выполнение скрипта.

Отладка

После ввода команды cont приложение будет запущено.

Отладка

Теперь отладчик остановит выполнение скрипта при обращении к маршруту / на строке debugger ;.

Отладка

Чтобы узнать значения переменных в точке останова скрипта используйте в качестве команды функцию watch() , которой передается имя переменной, значение которой необходимо узнать.

Теперь чтобы вывести значение желаемой переменной используйте команду watchers , которая выводит значения всех выражений, которые были заданы функцией watch() .

Для удаления переменной из списка отслеживаемых выражений имеется функция unwatch() .

Отладка

Для запуска последней выполненной команды просто нажмите Enter.

Точка останова уже в процессе отладки может быть установлена с помощью функции setBreakpoint() , которая передается один, либо два аргумента. Если один — то указывается номер строки в текущем скрипте, на которой необходимо остановить его выполнение.

Отладка

Если передать setBreakpoint() два параметра, то первым указывается имя файла, где необходимо установить точку останова, а вторым — номер строки в этом файле.

Отладка Node js в Chrome¶

Процессу отладки назначается уникальный идентификатор ( UUID ), входящий в состав URL, по которому к нему можно обратиться через браузер Google Chrome. UUID будет выведен сразу после начала выполнения команды запуска приложения в режиме отладки.

Отладка

Перейдя по ссылке chrome-devtools://devtools/bundled/js_app.html?experiments=true&v8only=true&ws=127.0.0.1:9229/81ed051b-e1d0-4415-b473-5a5d543dd306 , где 81ed051b-e1d0-4415-b473-5a5d543dd306 это ваш UUID , можно осуществлять отладку Node.js приложения отладчиком Chrome.

После перехода по ссылке вы должны увидеть следующее.

Отладка

Если версия браузера Google Chrome 67 и выше, то в URL app_js.html необходимо заменить на inspector.html .

Отладка Express

Для просмотра всех внутренних протоколов, используемых в Express, при запуске приложения задайте для переменной среды DEBUG значение express:* .

$ DEBUG=express:* node index.js 

В Windows используется соответствующая команда.

> set DEBUG=express:* & node index.js 

При запуске этой команды в стандартном приложении, созданном с помощью генератора приложений Express, будет получен следующий вывод:

$ DEBUG=express:* node ./bin/www express:router:route new / +0ms express:router:layer new / +1ms express:router:route get / +1ms express:router:layer new / +0ms express:router:route new / +1ms express:router:layer new / +0ms express:router:route get / +0ms express:router:layer new / +0ms express:application compile etag weak +1ms express:application compile query parser extended +0ms express:application compile trust proxy false +0ms express:application booting in development mode +1ms express:router use / query +0ms express:router:layer new / +0ms express:router use / expressInit +0ms express:router:layer new / +0ms express:router use / favicon +1ms express:router:layer new / +0ms express:router use / logger +0ms express:router:layer new / +0ms express:router use / jsonParser +0ms express:router:layer new / +1ms express:router use / urlencodedParser +0ms express:router:layer new / +0ms express:router use / cookieParser +0ms express:router:layer new / +0ms express:router use / stylus +90ms express:router:layer new / +0ms express:router use / serveStatic +0ms express:router:layer new / +0ms express:router use / router +0ms express:router:layer new / +1ms express:router use /users router +0ms express:router:layer new /users +0ms express:router use / <anonymous> +0ms express:router:layer new / +0ms express:router use / <anonymous> +0ms express:router:layer new / +0ms express:router use / <anonymous> +0ms express:router:layer new / +0ms 

При последующем запросе, адресованном приложению, вы увидите протоколы, заданные в коде Express:

 express:router dispatching GET / +4h express:router query : / +2ms express:router expressInit : / +0ms express:router favicon : / +0ms express:router logger : / +1ms express:router jsonParser : / +0ms express:router urlencodedParser : / +1ms express:router cookieParser : / +0ms express:router stylus : / +0ms express:router serveStatic : / +2ms express:router router : / +2ms express:router dispatching GET / +1ms express:view lookup "index.pug" +338ms express:view stat "/projects/example/views/index.pug" +0ms express:view render "/projects/example/views/index.pug" +1ms 

Для просмотра протоколов только из реализации маршрутизатора, задайте для переменной DEBUG значение express:router . Аналгичным образом, для просмотра протоколов только из реализации приложения, задайте для переменной DEBUG значение express:application и т.д.

Приложения, генерируемые с помощью команды express

Приложение, генерируемое с помощью команды express , также использует модуль debug , и область действия пространства имен отладки определяется именем приложения.

Например, если приложение сгенерировано с помощью команды $ express sample-app , операторы отладки (операторы debug) можно активировать с помощью следующей команды:

$ DEBUG=sample-app:* node ./bin/www 

Можно указать несколько пространств имен для отладки, путем ввода списка имен через запятую:

$ DEBUG=http,mail,express:* node index.js 

Отладчик¶

АПИ является удовлетворительным. Совместимость с NPM имеет высший приоритет и не будет нарушена кроме случаев явной необходимости.

Node.js включает в себя утилиту отладки командной строки. Клиент отладчика Node.js не является полнофункциональным отладчиком, но простой степпинг и инспекция возможны.

Чтобы воспользоваться им, запустите Node.js с аргументом inspect , за которым следует путь к отлаживаемому скрипту.

 1 2 3 4 5 6 7 8 9 10 11 12 13 14
$ node inspect myscript.js  < Debugger listening on ws://127.0.0.1:9229/621111f9-ffcb-4e82-b718-48a145fa5db8 < For help, see: https://nodejs.org/en/docs/inspector connecting to 127.0.0.1:9229 . ok  < Debugger attached.  ok Break on start in myscript.js:2 1 // myscript.js > 2 global.x = 5; 3 setTimeout(() =>  4 debugger; debug> 

Отладчик автоматически прерывается на первой исполняемой строке. Чтобы вместо этого он работал до первой точки останова (заданной оператором debugger ), установите переменную окружения NODE_INSPECT_RESUME_ON_START в 1 .

 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
$ cat myscript.js // myscript.js global.x = 5; setTimeout(() =>  debugger; console.log('world'); >, 1000); console.log('hello'); $ NODE_INSPECT_RESUME_ON_START=1 node inspect myscript.js  < Debugger listening on ws://127.0.0.1:9229/f1ed133e-7876-495b-83ae-c32c6fc319c2 < For help, see: https://nodejs.org/en/docs/inspector connecting to 127.0.0.1:9229 . ok  < Debugger attached.  < hello break in myscript.js:4 2 global.x = 5; 3 setTimeout(() => > 4 debugger; 5 console.log('world'); 6 >, 1000); debug> next break in myscript.js:5 3 setTimeout(() =>  4 debugger; > 5 console.log('world'); 6 >, 1000); 7 console.log('hello'); debug> repl Press Ctrl+C to leave debug repl > x 5 > 2 + 2 4 debug> next  < world break in myscript.js:6 4 debugger; 5 console.log('world'); > 6 >, 1000); 7 console.log('hello'); 8 debug> .exit $ 

Команда repl позволяет оценивать код удаленно. Команда next переходит к следующей строке. Введите help , чтобы узнать, какие еще команды доступны.

Нажатие enter без ввода команды повторит предыдущую команду отладчика.

Наблюдатели¶

Во время отладки можно следить за значениями выражений и переменных. В каждой точке останова каждое выражение из списка наблюдателей будет оценено в текущем контексте и отображено непосредственно перед листингом исходного кода точки останова.

Чтобы начать наблюдение за выражением, введите watch(‘my_expression’) . Команда watchers выведет активные наблюдатели. Чтобы удалить наблюдателя, введите unwatch(‘my_expression’) .

Ссылка на команду¶

Шаги¶

  • cont , c : Продолжить выполнение
  • next , n : Следующий шаг
  • step , s : Шаг в
  • out , o : Шаг наружу
  • pause : Приостановить выполнение кода (как кнопка паузы в Инструментах разработчика)

Точки останова¶

  • setBreakpoint() , sb() : Установить точку останова на текущей строке
  • setBreakpoint(line) , sb(line) : Установить точку останова на определенной строке
  • setBreakpoint(‘fn()’) , sb(. ) : Установка точки останова на первом утверждении в теле функции
  • setBreakpoint(‘script.js’, 1) , sb(. ) : Установить точку останова на первой строке файла script.js .
  • setBreakpoint(‘script.js’, 1, ‘num < 4') , sb(. ) : Устанавливает условную точку останова на первой строке script.js , которая прерывается только тогда, когда num < 4 оценивается в true .
  • clearBreakpoint(‘script.js’, 1) , cb(. ) : Очистить точку останова в script.js на строке 1

Также можно установить точку останова в файле (модуле), который еще не загружен:

 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
$ node inspect main.js  < Debugger listening on ws://127.0.0.1:9229/48a5b28a-550c-471b-b5e1-d13dd7165df9 < For help, see: https://nodejs.org/en/docs/inspector connecting to 127.0.0.1:9229 . ok  < Debugger attached. Break on start in main.js:1 > 1 const mod = require('./mod.js'); 2 mod.hello(); 3 mod.hello(); debug> setBreakpoint('mod.js', 22) Warning: script 'mod.js' was not loaded yet. debug> c break in mod.js:22 20 // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 >22 exports.hello = function()  23 return 'hello from module'; 24 >; debug> 

Также можно установить условную точку останова, которая прерывается только тогда, когда заданное выражение оценивается в true :

 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
$ node inspect main.js  < Debugger listening on ws://127.0.0.1:9229/ce24daa8-3816-44d4-b8ab-8273c8a66d35 < For help, see: https://nodejs.org/en/docs/inspector connecting to 127.0.0.1:9229 . ok  < Debugger attached.Break on start in main.js:7 5 > 6 > 7 addOne(10); 8 addOne(-1); 9 debug> setBreakpoint('main.js', 4, 'num < 0') 1 'use strict'; 2 3 function addOne(num) > 4 return num + 1; 5 > 6 7 addOne(10); 8 addOne(-1); 9 debug> cont break in main.js:4 2 3 function addOne(num) > 4 return num + 1; 5 > 6 debug> exec('num') -1 debug> 

Информация¶

  • backtrace , bt : Печать обратной трассировки текущего кадра выполнения
  • list(5) : Вывести исходный код скриптов с контекстом в 5 строк (по 5 строк до и после)
  • watch(expr) : Добавить выражение в список наблюдения
  • unwatch(expr) : Удалить выражение из списка наблюдения
  • unwatch(index) : Удалить выражение с определенным индексом из списка наблюдения
  • watchers : Список всех наблюдателей и их значений (автоматически выводится на каждой точке останова)
  • repl : Открыть repl отладчика для оценки в контексте отладочного скрипта
  • exec expr , p expr : Выполнить выражение в контексте отладочного скрипта и вывести его значение
  • profile : Начать сеанс профилирования процессора
  • profileEnd : Остановить текущий сеанс профилирования процессора
  • profiles : Список всех завершенных сеансов профилирования процессора
  • profiles[n].save(filepath = ‘node.cpupuprofile’) : Сохранить сессию профилирования процессора на диск в формате JSON
  • takeHeapSnapshot(filepath = ‘node.heapsnapshot’) : Сделать снимок кучи и сохранить на диск в формате JSON

Контроль исполнения¶

  • run : Запуск скрипта (автоматически запускается при старте отладчика)
  • restart : Перезапустить скрипт
  • kill : Убить скрипт

Различные¶

  • scripts : Список всех загруженных скриптов
  • version : Отображение версии V8

Расширенное использование¶

Интеграция инспектора V8 для Node.js¶

Интеграция V8 Inspector позволяет прикрепить Chrome DevTools к экземплярам Node.js для отладки и профилирования. Он использует протокол Chrome DevTools Protocol.

V8 Inspector можно включить, передав флаг —inspect при запуске приложения Node.js. Также можно указать пользовательский порт с этим флагом, например, —inspect=9222 будет принимать соединения DevTools на порту 9222.

Чтобы прервать работу на первой строке кода приложения, передайте флаг —inspect-brk вместо —inspect .

1 2 3
$ node --inspect index.js Debugger listening on ws://127.0.0.1:9229/dc9010dd-f8b8-4ac5-a510-c1a114ec7d29 For help, see: https://nodejs.org/en/docs/inspector 

(В приведенном примере UUID dc9010dd-f8b8-4ac5-a510-c1a114ec7d29 в конце URL генерируется на лету, он меняется в разных сеансах отладки).

Если браузер Chrome старше 66.0.3345.0, используйте inspector.html вместо js_app.html в приведенном выше URL.

Chrome DevTools пока не поддерживает отладку рабочих потоков. Для их отладки можно использовать ndb.

Отладка Node.js в Visual Studio

image

Мне нравится Node.js, но совсем недавно я начал использовать её на серьёзных проектах. При этом я продолжил пользоваться моей любимой средой разработки*. И если для простых скриптов достаточно просто редактора, то для серьёзной разработки нужны серьёзные инструменты, в частности, дебаггер. Ничего готового для студии найти не удалось, поэтому я взял и за пару выходных сделал его сам:

Quick start

Устанавливаем Node.js и экстеншен NodeVsDebugger. Также его можно установить из галереи расширений: Alt+T,U выбираем Online, вводим в поиск “node debug” и давим download.
Далее открываем js-документ, ставим брейкпоинт, давим Ctrl+E,Ctrl+D, вуаля!

Что уже готово

  • Уже есть Run/Step into/Step over/Step out/Run to line, Locals, Watches, инспекция любых идентификаторов в редакторе, Custom inspector для строк и ещё всякое по-мелочи;
  • Отладка отдельно болтающихся скриптов (Ctrl+E,Ctrl+D);
  • Отладка проекта с автоматическим выбором дебаггера (Ctrl+E,Ctrl+A, можно смело перебиндить на F5: проекты не настроенные на отладку в ноде это не затронет);
  • Просмотр (и отладка) встроенных в ноду скриптов (pic);
  • Подключение к ноде на удалённом хосте (только для проектов);
  • Маппинг удалённых скриптов, если удалённая папка подключена, например, через SAMBA или SSHFS.

image

Отладка на удалённом хосте
Также можно отлаживать ноду запущенную на другом хосте, например на виртуальной машине с линуксом. Достаточно тем или иным образом пробросить удалённый порт и прописать это в настройках. Пример для PuTTY:

Чего пока не хватает

  • Редактирования значений;
  • Нормального отображения функций (с исходным кодом);
  • Code completion в watches;
  • Поддержки Immediate window;
  • Поддержки окна Autos, и отображения там контекстов;
  • Показывать где на самом деле был установлен брейкпоинт;
  • Условия и hit-count в брейкпоинтах;
  • Переключателя режима отлова эксепшенов;
  • Полноценного Attach to process для ноды, запущенной без —debug;
  • Нормального подключения к удалённой ноде (сейчас запускается фейковый процесс cmd.exe);
  • Отладки node cluster;
  • Edit and Continue (оно же LiveEdit в V8);
  • Отладки нативного кода и JS в одном экземпляре студии;
  • Отладки JS в хроме и производных от него браузерах (там тот же дебаг-протокол);
  • Поддержки source maps и отладки typescript;
  • Пакета для VS 2010;
  • Иконок.

Как это делалось

В ноде уже есть готовый бэкенд для дебаггера, к которому можно достучаться просто подключившись по http-подобному протоколу на порт 5858 (по-умолчанию). Протокол основан на обмене сообщениями в JSON-формате. Сам протокол не является нодо-специфичным, а унаследован от движка V8. Краткое описание можно найти здесь. Этого документа достаточно для первичного знакомства, однако, как выяснилось позже, там полно ошибок, недосказанности, и тупо опечаток.
В студии же есть готовый хороший GUI для дебаггера. Естественно, я решил не изобретать велосипед, а просто соединил эти две части. Интерфейсы и понятия обоих частей довольно похожи, ведь тут тяжело изобрести что-то новое. В протоколе V8 есть команды управления отлаживаемым процессом (Break, Go, Step), есть опрос текущего состояния (Stacktrace он же Backtrace), есть какие-то методы для просмотра переменных, выражений и выполнения кусочков кода. Да и много чего другого…

image

С какими проблемами я столкнулся:
— При первой же попытке отладки http-сервера я нарвался на зависание дебаггера при каждом шаге отладки. Виноват в этом был километровый backtrace, возникший из-за хитрой сериализации (она называется mirror), которая пытается сериализовать Buffer с отдельным описательным объектом на каждый байт буффера. Починилось это включением inlineRefs.
— По-умолчанию строки показываются сокращёнными, как на следующем скриншоте. Ничего плохого в этом нет, благо целиком строку можно тоже просмотреть: по запросу пользователя мы идём за строкой с maxStringLength = -1

Всё это можно понять, заглянув в debug-debugger.js там же можно посмотреть и другие команды, не описанные в документе на вики.
— Со стороны студии проблемой было отсутствие адекватных примеров по Custom Debug Engine. У меня не было времени на детальное изучение документации в MSDN (а там 100500 интерфейсов, структур, енумов и прочего), хотя в итоге я туда часто поглядывал. Единственный официальный пример — это написанный под старую студию простенький дебаггер нативного C, автоматически сконверченный в проект 2010 студии. Он состоит из большого куска на managed C++, который нам не нужен. И, собственно, обёрток над дебажными интерфейсами студии, написанными во времена C#2.0, ужасно многословно.
— Регистрацию движка я переделал, причём дважды. В примере регистрация движка была сделана старым regsvr32 «mixed-mode C++ сборка». От этой сборки мы избавились, и регистрацию я сделал просто явным добавлением ключей в реестр. Но с регистрацией тоже было не всё так просто, у 2012й студии немного поменялись места, где она ищет движки для отладки. В итоге всё само собой полечилось, когда я сделал пакет (package) для дебаггера и добавил ему RegistrationAttribute.
— Чехарда с названием кнопок, пунктов меню и команд. Разобраться помогла эта статья.
— Вместо стандартной библиотеки для парсинга/сериализации JSON я взял Newtonsoft.Json. Она мне больше нравится, да и работает шустрее.

Вместо послесловия

Скачивайте, пробуйте, пишите (хорошие) отзывы на странице экстеншена )
Баги, пожалуйста, отправляйте на гитхаб.
А ещё лучше — присылайте пулл-реквесты прямо к коду — fork me on GitHub!.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *