node-inspector: debug and step through child_process - node.js

I have been using node-inspector to step through my code and I like it.
However, I am not able to step through forked processes :
... my code ...
var a = getValue();
var b = func1(a);
var command = 'myCommand.js';
child_process.spawn(command, [args], [options]);
I am able to step through code until I reach the child_process statement. Is there a way to step into that function and debug the execution of the command ?

Debugging forked processes is not supported out of the box.
You need to:
Instruct the forked process to start the debugger and to start it on a different port than the master process is listenging. See Node's lib/cluster.js for an example how to implement this part.
Open a new instance of Node Inspector UI (front-end) to debug the child process. You can reuse the same Node Inspector server, just change the value of the ?port= parameter to match the port where the debugger in your child process is listening on.

Related

Node.js process doesn't exit when run under pm2

I have a node.js script that runs and exits fine in console, but it doesn't exit unless I call process.exit() in pm2. PM2 config is:
{
name: "worker",
script: "./worker.js",
restart_delay: 60000,
out_file: "/tmp/worker.log",
error_file: "/tmp/worker_err.log"
},
I've installed why-is-node-running to see what keeps the process running in 10 seconds after the expected exit and the output is:
There are 9 handle(s) keeping the process running
# TLSWRAP
node:internal/async_hooks:200
# TLSWRAP
node:internal/async_hooks:200
# ZLIB
node:internal/async_hooks:200
/Users/r/code/app/node_modules/decompress-response/index.js:43 - const decompressStream = isBrotli ? zlib.createBrotliDecompress() : zlib.createUnzip();
file:///Users/r/code/app/node_modules/got/dist/source/core/index.js:586
file:///Users/r/code/app/node_modules/got/dist/source/core/index.js:768
file:///Users/r/code/app/node_modules/got/dist/source/core/index.js:786
# TLSWRAP
node:internal/async_hooks:200
# ZLIB
node:internal/async_hooks:200
/Users/r/code/app/node_modules/decompress-response/index.js:43 - const decompressStream = isBrotli ? zlib.createBrotliDecompress() : zlib.createUnzip();
file:///Users/r/code/app/node_modules/got/dist/source/core/index.js:586
file:///Users/r/code/app/node_modules/got/dist/source/core/index.js:768
file:///Users/r/code/app/node_modules/got/dist/source/core/index.js:786
# TLSWRAP
node:internal/async_hooks:200
# ZLIB
node:internal/async_hooks:200
/Users/r/code/app/node_modules/decompress-response/index.js:43 - const decompressStream = isBrotli ? zlib.createBrotliDecompress() : zlib.createUnzip();
file:///Users/r/code/app/node_modules/got/dist/source/core/index.js:586
file:///Users/r/code/app/node_modules/got/dist/source/core/index.js:768
file:///Users/r/code/app/node_modules/got/dist/source/core/index.js:786
# TLSWRAP
node:internal/async_hooks:200
# Timeout
node:internal/async_hooks:200
node:internal/async_hooks:468
node:internal/timers:162
node:internal/timers:196
file:///Users/r/code/app/worker.js:65
node:internal/process/task_queues:94
Why doesn't node exit? How do I further debug this?
PS: Sorry for a large paste
UPDATE
I've managed to reproduce this in a comically small 2-liner:
import got from "got";
await got.post('https://anty-api.com/browser_profiles', {form: {a: 123}}).json();
The above code throws as expected when run form console, yet keeps running forever when called by pm2.
UPDATE 2
It does reproduce with an empty app file too.
I think this is just the way pm2 works. You can expect that, when running under pm2, the node process will continue to run forever, (whether your app is responsible for pending async event sources or not) unless you either crash or do something to explicitly terminate it such as process.exit().
As you've discovered, this has nothing to do with any code in your app.js. Even an empty app.js exhibits this behaviour. This is a fundamental design aspect of pm2. It wraps your program and it's the wrapper that is keep the node process alive.
This is because pm2 runs your program (in forked mode, as opposed to cluster mode) by launching a node process that runs ProcessContainerFork.js (the wrapper). This module establishes and maintains a connection to pm2's managing process (a.k.a "god daemon") and loads your app's main module with require('module')._load(...). The communication channel will always count as an event source that keeps the actual node process alive.
Even if your program does nothing, the status of your program will be "online". Even if your program reaches the state where, had it been launched directly, node would have exited, the state is still "online" in this case because of the wrapper.
This leaves the designers of pm2 with the challenge of trying to know if your program is no longer responsible for any events (in which case node would normally exit). pm2 doesn't have the feature to distinguish between reasons node is being kept alive due to code you wrote in your app.js vs reasons node is being kept alive due to the infrastructure established by ProcessContainerFork.js. One could certainly imagine that pm2 could use async_hooks to keep track of event sources originating from your app rather than from ProcessContainerFork.js (much like how why-is-node-running does), and then tearing down properly when it reaches this state. Perhaps pm2 chooses not to do this to avoid the performance penalty associated with async hooks? Perhaps an app that exits on purpose but is intended to be restarted seems too much like a cron job? I'm speculating yours is not the primary use case for pm2. I suppose you could make a feature request and see what the pm2 authors have to say about it.
I think this means if you want to gracefully exit and have pm2 restart your program, you'll need to call process.exit to do so. You won't be able to rely on node knowing that there are no more event sources because pm2 is responsible for some of them. You will, of course, have to ensure that all your relevant pending promises or timers have resolved before calling process.exit because that will immediately terminate the process without waiting for pending things to happen.

How to restart a Node.js application and handover the new process to the console

The following Node.js script can restart itself and will even still print to the correct console (or terminal if you prefer), but it will no longer be running in the foreground, as in you can't exit it with Ctrl+C anymore (see screenshot) etc:
console.log("This is pid " + process.pid);
setTimeout(function () {
process.on("exit", function () {
require("child_process").spawn(process.argv.shift(), process.argv, {
cwd: process.cwd(),
detached : true,
stdio: "inherit"
});
});
process.exit();
}, 5000);
I've already tried detached: true vs detached: false, but obviously this didn't solve the problem...
Is there a way to make the new node process run in the foreground, replacing the old one? Or this this not possible?
I know that in Bash you can pull a program back from the background like this:
$ watch echo "runs in background" &
$ fg # pulls the background process to the foreground
But I'm not looking for a Bash command or so, I'm looking for a programmatic solution within the Node.js script that works on any platform.
No, once a process has exited it cannot perform any more operations, and there's no such thing as a "please foreground this after I exit"-type API for terminals that I've ever heard of.
The proper way to solve this is via a wrapper which monitors your process for failures and restarts. The wrapper then has control of stdio and passes those to its children.
You could achieve this via a simple bash loop, another node script, or you might just be able to leverage the NodeJS cluster module for this.
As Jonny said, you'd need to have a process manager that handles the running of your application. Per the Node.js documentation for child_process, spawn() functions similar to popen() at the system level, which creates a forked process. This generally doesn't go into the foreground. Also, when a parent process exits, control is returned to either the calling process or the shell itself.
A popular process management solution is PM2, which can be installed via npm i -g pm2. (Not linking to their site/documentation here) PM2 is cross-platform, but it does require an "external" dependency that doesn't live within the codebase itself.
I would also be curious as to why you want a script that on exit restarts itself in the manner you're describing, since it seems like just re-running the script -- which is what PM2 and similar do -- would yield the same results, and not involve mucking around with process management manually.

Spawn process that keeps running after app.quit()

How can I launch a user-defined application from an electron application. After the application is launched the electron application should quit, and restart after the program has finished. This to free system resources.
I was hoping to be able to use a child_process.spawn with detach option and unref.
let command= 'notepad.exe && ./electron.exe'
const launched_application = child_process.spawn(command,[],{detached:true,stdio:'ignore',shell:true})
launched_application.unref()
app.quit()
When the code hits app.close() the child_process is also killed. Is there a way to keep the child_process running after app.quit()
This also gives an arror on the && part, which I'm planning to fix using a .bat file. Any recommendations regarding this are also welcome.
if you want to relaunch app again then you should just close or hide all windows and activate setInterval to keep checking if child process is alive or not with help of pid ...
after that you can create new window again or show hidden window... and if you want fresh process then you should relaunch app after you get callback of user quitting that child application....

Spawn new child process with own console window

I've got a parent application in node.js which needs to spawn multiple worker applications (also in node.js) applications according to need.
I've already got communication working between them - don't need to use any of the built-in node stuff.
Now the problem is that I'd like each worker process to have it's own console window - since I do a lot of writing to the console and I want to keep an eye on it.
I've looked through the Node child_process documentation, and it says that by setting options to detached:
On Windows, setting options.detached to true makes it possible for the child process to continue running after the parent exits. The child will have its own console window.
However when I use my own code
const Process = require("child_process").spawn;
Process(process.argv[0], ["myApplicationPath","otherArgs"],{detached: true,stdio: ['ignore']});
It doesn't work. The child application does spawn, but no console window turns up.
I'm a bit late here, but I just had to figure this out as well, so here is the answer for anyone else who is struggling with this:
I managed to spawn my child application in its own console using this:
childProcess.spawn("<cmd>", [], {shell: true, detached: true});
In addition to the {detached: true} what OP is using, I used {shell: true}. With the combination of both, I managed to spawn my child application with its own console.

Send command to running node process, get back data from inside the app

I start a node.js app per commandline in linux.
I see the app running, e.g. by entering "top".
Is there a way to send some command to the running app (maybe to the pid?) and get back info from inside it (maybe listen for some input and return requested info)?
Use repl module. There are examples in the doco doing exactly what you need: run JS in the context of your application and return output.
One simple solution is to use process signals. You can define a handler for a signal in your program to output some data to the console (or write to a file or to a database, if your application is running as a service not attached to a terminal you can see):
process.on('SIGUSR1', function() {
console.log('hello. you called?');
});
and then send a signal to it from your shell:
kill --signal USR1 <pid of node app.js>
This will invoke the signal handler you have defined in your node.js application.

Resources