console.logからの脱却 - node.jsでデバッグするには(Visual Studio Code編)


前回のおはなし

若き開発者ヒロロはNode.jsでバックエンド開発を任されたがデバッグ方法がわからず、苦戦。
自力で調査しChrome DevToolでデバッグする方法を見つけ出し、なんとかconsole.log地獄は免れた。
しかし、普段利用しているVSCodeのデバッグツールを活用してより快適なデバッグ環境を作ろうと決意する。

前回の詳細「console.logからの脱却 - node.jsでデバッグするには(Chrome DevTool編)」こちら

VSCodeでのデバッグ環境構築

公式ドキュメント(https://code.visualstudio.com/docs/editor/debugging) によればVSCodeでデバッグを有効にするにはあらかじめ設定ファイルに情報を記載する必要があるそうです。

VSCodeを起動してデバッグメニューを開き、「create a launch.json file.」をクリックします。
その後、「Node.js」の項目が出てくるので選択します。

すると、以下のようなファイルが生成されます。

launch.json
{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "type": "node",
            "request": "launch",
            "name": "Launch Program",
            "skipFiles": [
                "<node_internals>/**"
            ],
            "program": "${file}"
        }
    ]
}

requestの項目について

ここからこのファイルにデバッグ設定を記載していきますが今回の記事で重要なrequestについて。
ここにはLaunchAttachのいずれかが入ります。

Launchに設定すると、デバッグを実行したときに指定したコマンドを実行して即座にプログラムのデバッグをすることができます。

一方Aattachに設定すると、指定したポートを監視し、コマンドプロンプトやターミナルからデバッグモードでプログラムを実行することでデバッグすることができます。

いずれかの方法でないとデバッグできないというわけではなくどちらを使っても問題はありません。
どのように設定していくかは実際にあった私の事例をもとに取り上げていきます。

A. AWS Lambdaのコードをコマンドで実行したときにデバッグしたい

serverless frameworkでサーバーレスアプリケーションを開発していた時、TDD(テスト駆動開発)を採用していたので実装したらmochaで必ずテストをする必要がありました。
その際に失敗することがありますが、原因が何なのか特定しづらいときがありました。
その時はAttachパターンを使ってデバッグを行いました。

  1. launch.jsonを開き、画面右下に出てくる「Add Configuration...」ボタンを押し、「Node.js:Attach」を選択する。もしくは直接以下の設定を記載する。
launch.json
{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "type": "node",
            "request": "attach",
            "name": "Attach",
            "port": 9229,
            "skipFiles": [
                "<node_internals>/**"
            ]
        }
    ]
}
  1. ターミナルを開き、デバッグモードでmochaを実行する。
mocha test.js --inspect

mochaを実行すると指定したテストに紐づくコードが実行されるのでこれによって対象のコードにbreakpointを打って止めることができるようになりました。
Node.jsはデバッグポートはデフォルトで9229に設定されるので、変更する際はlaunch.jsonのポート番号もそれに合わせることを忘れずに。

B. Electronのmain processをデバッグしたい

Electronでの開発ではmain processと呼ばれるWebアプリでいうサーバーサイド的な部分があるのですが、ここでの開発では開始ボタンを押すと自動でプログラムが起動し、デバッグできるようにしたかったという背景があります。また、スムーズに開発できるようファイルを変更するとすぐさまリロードされるよう(ホットリロード)にもしたいという要望がありました。
その時はLaunchパターンを使うように設定しました。
1.事前にElectronElectromonをDevDependancyでインストールする。

npm install -D electron electromon

Electromonはホットリロードを実現するためのライブラリ。nodemonのElectronバージョンとでも考えてればわかりやすいと思う。

2.先と同じくAdd Configuration...ボタンからNode.js : Electron Mainを選択する。その後、"runtimeExecutable":"${workspaceFolder}/node_modules/.bin/electromon"
"args": ["--inspect=5858"]"port": 5858"restart": true
のプロパティを追加する。
最終的に以下のようになる。

launch.json
{
  // Use IntelliSense to learn about possible attributes.
  // Hover to view descriptions of existing attributes.
  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387

  // Debug settings for electron with 'electromon' so that it can be live reloaded on saving files.
  // For more information about setting, visit: https://code.visualstudio.com/docs/nodejs/nodejs-debugging#_restarting-debug-sessions-automatically-when-source-is-edited
  //
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Electron Main",
      "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electromon",
      "program": "${workspaceFolder}/main.js",
      "args": ["--inspect=5858"],
      "port": 5858,
      "restart": true,
      "skipFiles": ["<node_internals>/**"],
    }
  ]
}

3.VSCode上でデバッグ画面からRunを押す。するとruntimeExecutable, program, argsの設定により

electromon --inspect=5858 main.js

が起動する。さらにファイルを編集して保存すると"restart": trueにより再度デバッグが行われる。

プロセスが終了しない

ひとまずこれで設定が終わったので開発を進めていると、Electronが同時に複数起動する問題に遭遇しました。
アプリケーション終了時の挙動を確認したかったのですが、終了するとデバッグも終了してしまうため、何度も終了→デバッグ起動を繰り返しているとそのようなことになってしまいました。

ps -ef | grep electron

でプロセスを確認すると、まだプロセスが残っていたことがわかりました。どうやらアプリケーションを閉じるとデバッグも終了するがelectromonは生き続けるみたいです。
VSCode公式サイトにもこのような記載がありました。

Tip: Pressing the Stop button stops the debug session and disconnects from Node.js, but nodemon (and Node.js) will continue to run. To stop nodemon, you will have to kill it from the command line (which is easily possible if you use the integratedTerminal as shown above).

ちゃんと「デバッグは終了しますがnodemonは走り続けます」と書かれてますね...(こちらはnodemonですがelectromonと置き換えても問題ありません)

・electronを終了するとデバッグも終了してしまう
・デバッグが終了してもelectromonは走り続ける
ということからアプリを終了するたびに手動でelectromonも止めないといけないということですね。

ご親切にintegrationTerminalを設定すると簡単にプロセスの終了ができますと書いてくれてましたのでそれに合わせてlaunch.json
"console" : "integrationTerminal"を追記してみました。
起動すると、VSCodeのターミナルにelectromonのコンソールが立ち上がるようになりました。
プロセスを終了する際はこのターミナルを終了してくださいということですね。

まとめ

これでVSCodeを使ってNode.jsをデバッグできるようになり、しかもそれぞれの利用シーンに応じてAttachパターンとLaunchパターンを使い分けることができるようになりました。

あれからしばらく経ちましたがもうデバッグで悩むことがほとんどなくなりました。もうNode.jsでもconsole.log地獄に悩まされなくてすみそうです。