Monday, 11 February 2019

Debugging NodeJS child process' VSCode babel-node

I use babel-node for my runtimeExecutable in my launch.json as shown in this answer. The reason for doing so is because I am using ES6 imports and breakpoints in VSCode moved around due to transpilation and the source maps.

launch.json

{
    "version": "0.2.0",
    "configurations": [{
        "type": "node",
        "request": "launch",
        "name": "Debug",
        "autoAttachChildProcesses": true,
        "program": "${workspaceFolder}/index.js",
        "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/babel-node",
        "runtimeArgs": [
            "--nolazy"
        ],
        "env": {
            "BABEL_ENV": "debug"
        }
    }]
}

app.js (where I am forking the child processes)

(async () => {
    const process1 = fork(path.join(__dirname, "children", "process", "one.js"));
    const process2 = fork(path.join(__dirname, "children", "process", "two.js"));

    process1.send("start");
    process2.send("start");
})();

one.js/two.js

process.on("message", async (message) => {
    console.log("message - " + message);
    await init();
});

The content of the files is less important but thought I would put them there anyway. I can debug the IIFE of app.js perfectly fine. When skipping passed the lines where I fork new process', I get this error in the console:

error: unknown option `--inspect-brk'

I took the autoAttachChildProcesses rule from this answer but am assuming that babel-node has complicated things.

I have breakpoints in both one.js and two.js in the callbacks on the "message" event but they become unverified breakpoints when I initialise debugging.

EDIT

I have now changed to use NodeJS cluster module instead of child_process purely due to all the examples I have found are using cluster instead.

My now launch.json config:

{
    "type": "node",
    "request": "launch",
    "name": "Debug 2",
    "autoAttachChildProcesses": true,
    "stopOnEntry": false,
    "program": "${workspaceFolder}/index.js",
    "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/babel-node",
    "console": "internalConsole",
    "runtimeArgs": [
        "--nolazy"
    ],
    "env": {
        "BABEL_ENV": "debug"
    },
    "skipFiles": [
        "node_modules/**/*.js",
        "<node_internals>/**/*.js"
    ]
}

Project is initalises index.js:

require("dotenv").config();
console.log("::: index.js :::");

require("./src/app.js")

src/app.js

import express from "express";
import session from "express-session";
import bodyParser from "body-parser";
import morgan from "morgan";
import cors from "cors";
import chalk from "chalk";
import cluster from "cluster";

const app = express();
const log = console.log;
const numCpus = 4;
console.log("::: app.js :::");

console.log(`::: Master or Worker?: ${(cluster.isMaster) ? "Master" : "Worker"}`);
if (cluster.isMaster) {
    app.use(bodyParser.json());
    app.use(morgan("combined"));
    app.use(cors());
    app.use(session({
        secret: "test",
        resave: false,
        saveUninitialized: true,
    }));

    app.listen(process.env.PORT || 3000, () => {
        log(chalk.green("--------------------"));
        log(chalk.green(`Host:\t${process.env.HOST || "localhost"}`));
        log(chalk.green(`Port:\t${process.env.PORT || 3000}`))
        log(chalk.green("--------------------"));
    });

    for (let i = 0; i < numCpus; i++) {
        console.log("::: forking :::");
        cluster.fork();
    }

    cluster.on("online", (worker) => {
        console.log(`Worker ${worker.id} is now online after it has been forked`);
    });
    cluster.on("listening", (worker, address) => {
        console.log(`A worker is now connected to ${address.address}:${address.port}`);
    });
    cluster.on("fork", (worker) => {
        console.log(`New worker being forked: ${worker.id}`)
    });
    cluster.on("exit", (worker, code, signal) => {
        console.log(`Worker ${worker.id} died ${signal || code}`);
    });
    cluster.on("death", (worker) => {
        console.log(`Worker ${worker.id} died`)
    });
} else {
    require("./worker.js")
}

export default app;

src/worker.js

console.log("I'M A NEW WORKER!")

If, from the terminal, I run npm run start:dev which runs:

NODE_ENV=development $(npm bin)/babel-node index.js

I get the output:

enter image description here

which seems correct to me so the setup of the cluster seems correct.

When attempting to debug this, however, I get different results and the breakpoints in the else condition are never hit along with "I'M A NEW WORKER!" is never logged. The command when debugging is:

babel-node --nolazy --inspect-brk=33597 index.js

I've placed breakpoints around src/app.js and index.js. Initially, all seems okay but after the for loop is complete and both cluster.fork()s have been run, a strange thing happens. The debug goes back and hits index.js for the parent process. Before this, the child processes exist in the call stack but only for a set amount of time (but no console logs saying they have exited). After this, the debug says it is still running but no breakpoint is ever hit. The breakpoints in src/worker.js is unverified. As a result, all the logs I see in the console are:

enter image description here



from Debugging NodeJS child process' VSCode babel-node

No comments:

Post a Comment