2

Note: I've already seen SIGINT handler in NodeJS app not called for ctrl-C (Mac) but it seems it's not my case.

I have a Node.js application - a Discord bot that uses Pino for logging. Main file, app.js is like this:

...
const logger = pino();
const client = new Discord.Client();

process
    .on('SIGINT', () => {
        console.log('SIGINT');

        client.destroy();
        logger.info('Exiting');
    })
    .on('beforeExit', () => { console.log('beforeExit'); }
    .on('exit', () => { console.log('exit'); }

client.on('ready', () => {
    logger.info(`Logged in`);
});

client.login(process.env.TOKEN);

when I start it just with node ./dist/app.js and press ctrl-c, it works - I get "SIGINT" from console.log(), "Exiting" from logger and "beforeExit", "exit" from console again. But I want to use a run script to redirect logging output to various files and to console:

#!/bin/bash
node ./dist/app.js | \
tee -a \
  ./logs/complete.log \
  >(jq -cM --unbuffered 'select(.level == 40)' >> ./logs/user.log) \
  >(jq -cM --unbuffered 'select(.level >= 50)' >> ./logs/error.log)  | \
./node_modules/.bin/pino-pretty --levelFirst --ignore hostname,pid,ctx --translateTime SYS:standard

But now when I press ctrl-c it just exits silently, "SIGINT" handler is not activated at all. Same for "beforeExit" and "exit". It seems that ctrl-c is intercepted somewhere down the pipe.

So, how to make the mentioned handlers work? Also, could anyone offer an explanation why this happens and - if it's really something with the pipe - how breaking a pipe works in regard to initiating process?

Forseti
  • 2,587
  • 6
  • 21
  • 32
  • 1
    Possibly answered here: https://stackoverflow.com/a/30522678/269448. If yes, your process is killed by SIGPIPE before even has chance to receive SIGINT. – Zbigniew Zagórski Jul 27 '20 at 09:18
  • Have you tried ignoring SIGINT for `tee`, like `tee -ai ...`? I'm asking because the order each process in the pipeline receives SIGINT is uncertain. I suspect the issue here is that `tee` dies before `node` starts handling SIGINT. – oguz ismail Jul 27 '20 at 09:20
  • @oguzismail: thanks for suggestion! I tried it and results are somewhat strange. "exit" and "beforeExit" never fire. In "SIGINT" I added `console.log()` after `client.destroy()` and another after `logger.info('Exiting');` and they aren't reached. But the first `console.log()` in "SIGINT" produces "SIGINT" output normally. – Forseti Jul 27 '20 at 09:56
  • @ZbigniewZagórski: I changed "SIGINT" handler into "SIGPIPE" handler - wasn't fired at all. – Forseti Jul 27 '20 at 10:58
  • @oguzismail: I'm almost there! I used `tee -api --output-error=warn` and I got all the handlers firing. Still, I'm not sure if it's ok - I get two `tee: /dev/fd/XX : broken pipe` messages in the console. Is it ok to drop that `--output-error=warn` and ignore warnings? – Forseti Jul 27 '20 at 11:12
  • 1
    The two JQ processes receive the SIGINT as well and die immediately; then `tee` receives a SIGPIPE while writing to them and terminates. Specifying `-p` lets `tee` ignore SIGPIPE, and keep copying its input to stdout and `./logs/complete.log`. If you want only `node` to receive the SIGINT, why don't you just try something like `node ... | { trap '' INT; ...; }` – oguz ismail Jul 27 '20 at 11:22
  • @oguzismail: as the deployment server is on FreeBSD and its `tee` lacks `-p` option, I had to switch to `trap` method. It works, thanks! If you'd like to post an answer, I'd accept it :) – Forseti Jul 27 '20 at 16:37

0 Answers0