1

I am attempting to catch 'Uncaught Exception's and write them to a file on the server. My code is:

var cluster = require('cluster');
// The master's job to spawn workers initially and when they die

if (cluster.isMaster) {
    // Get the number of processor cores
    var cpuCount = require('os').cpus().length;

    // Create a worker for each CPU
    for (var i = 0; i < cpuCount; i += 1) {
        cluster.fork();
    }

    // When a worker exits, fork a new one
    cluster.on('exit', function(worker) {
        console.log('Worker %d died', worker.id);
        cluster.fork();
    });
} else if (cluster.isWorker) {
    const _express = require('express');
    const _bodyParser = require('body-parser');
    const _cors = require('cors');
    const _fs = require('fs');
    const _util = require('util');
    const _app = _express();
    const _http = require('http');
    const _server = _http.createServer(_app);
    const _port = 80;

    process.on('uncaughtException', function(_err) {
        var _error = _fs.createWriteStream(__dirname + '/node.error.log', { flags: 'a' });

        _error.write(_err.message + '\n', (err) => {
            if (err) {
                console.log('Error:', err.message);
            }else{
                console.log('Error Written');
            }
        });

        _error.end();

        console.log('Caught exception: ' + _err);
    });var _listener = _server.listen(_port, () => {
        console.log('Server started on port ' + _port);
    });

    const _io = require('socket.io')(_listener);

    // parse application/json
    _app.use(_bodyParser.json());

    // parse application/x-www-form-urlencoded
    _app.use(_bodyParser.urlencoded({ extended: true }));

    _app.use(_express.static(__dirname + '/node_modules'));

    // Allow cross-origin requests
    var corsOptions = {
        origin: function(_origin, _callback){
            _callback(null, _origin);
        },
        credentials: true
    };

    _app.use(_cors(corsOptions));

    // html render engine
    _app.engine('html', require('ejs').renderFile);
    _app.set('view engine', 'html');

    _io.on('connection', function (_socket) {
        // Sends to the initiator
        _socket.emit("connectionSuccessful", "Connected on socket " + _socket.id);

        _socket.on('registerUser', function(_args) {
            console.log('registerUser', _args);
            fakeFunction();
        });
    });
}

The registerUser event is not my full event, but shortened just to throw an error.

Original

The console log at the bottom is printed to the command line (the caught exception line). The error file is created (if not already). However, nothing inside of the _error.write area is executed. No console logs are displayed, and the file remains empty.

It is not a permissions issue, as I have given everyone (666) access to read and write to the file (for testing). So I am not sure why this is not working. All of the examples I've seen show that this works for them. I am not sure why it does not work for me.

EDIT

Here is what I am seeing in the console:

Server started on port 80
Connected
Caught exception: TypeError: Cannot read property 'multi_id' of undefined
TypeError: Cannot read property 'multi_id' of undefined
    at Query._callback (/var/www/nodejs/models/auctions.js:41:18)
    at Query.Sequence.end (/var/www/nodejs/node_modules/mysql-clusterfarm/lib/protocol/sequences/Sequence.js:85:24)
    at Query._handleFinalResultPacket (/var/www/nodejs/node_modules/mysql-clusterfarm/lib/protocol/sequences/Query.js:144:8)
    at Query.EofPacket (/var/www/nodejs/node_modules/mysql-clusterfarm/lib/protocol/sequences/Query.js:128:8)
    at Protocol._parsePacket (/var/www/nodejs/node_modules/mysql-clusterfarm/lib/protocol/Protocol.js:280:23)
    at Parser.write (/var/www/nodejs/node_modules/mysql-clusterfarm/lib/protocol/Parser.js:74:12)
    at Protocol.write (/var/www/nodejs/node_modules/mysql-clusterfarm/lib/protocol/Protocol.js:39:16)
    at Socket.<anonymous> (/var/www/nodejs/node_modules/mysql-clusterfarm/lib/Connection.js:109:28)
    at emitOne (events.js:116:13)
    at Socket.emit (events.js:211:7) 'Uncaught Exception thrown'
Worker 1 died

RESOLVED

I found that I was actually catching uncaughtExceptions twice...

process
    .on('unhandledRejection', (reason, p) => {
        console.error(reason, 'Unhandled Rejection at Promise', p);
        console.log(reason, 'Unhandled Rejection at Promise', p);
        process.exit(1);
    })
    .on('uncaughtException', _err => {
        console.error(_err, 'Uncaught Exception thrown');
        console.log(_err, 'Uncaught Exception thrown');
        process.exit(1);
    });

This was causing the previous method to die before it could actually write the error to the file (due to process.exit(1)).

James
  • 3,765
  • 4
  • 48
  • 79
  • 1
    Which node version are you using? Because that snippet works for me. – Marcos Casagrande Jun 22 '18 at 16:05
  • @MarcosCasagrande It's v8.11.3 – James Jun 22 '18 at 16:14
  • Weird, I'm using `8.9`. – Marcos Casagrande Jun 22 '18 at 16:15
  • @MarcosCasagrande Yes, I do not understand it at all. There are so many examples out, even from last year, using this code (essentially). But it just does not write anything for me. – James Jun 22 '18 at 16:16
  • Can you put the following error handler before `.end`: `_error.on('error', console.error);` – Marcos Casagrande Jun 22 '18 at 16:22
  • @MarcosCasagrande I tried that, but it did not put anything new in to the console. I'll edit my question to show what I am receiving, though it does not include anything new here. – James Jun 22 '18 at 16:24
  • Ok, I see your problem now. You're using cluster module? or what code is triggering: `worker 1 died`. Your snippet by itself works, but you have something else going on. show more code, or a full snippet where we can replicate the error, so we can help you. Right now the error doesn't ocurr in thtat code, so we can only speculate until we see the real code. – Marcos Casagrande Jun 22 '18 at 16:26
  • @MarcosCasagrande I've updated the code. – James Jun 22 '18 at 16:32
  • 1
    Can't seem to trigger `worker died`, can you provide a snippet where it triggers, without the need of a server or socket.io, a snippet that I can just run without dependencies, since those dependencies are not the issue here. – Marcos Casagrande Jun 22 '18 at 16:37
  • @MarcosCasagrande You can add `process.exit(1)` anywhere within the `} else if (cluster.isWorker) {` section to get it to exit and throw that particular message. – James Jun 22 '18 at 16:40
  • @MarcosCasagrande I found the issue. I seen it when I got that code snippet for you. I had another section for when an error was thrown as well, which was terminating the code completely (before it got to write to the file, I assume). – James Jun 22 '18 at 16:43
  • That's the problem, if the code is exiting before it can write, since the write is `async` nothing will be written. – Marcos Casagrande Jun 22 '18 at 16:48
  • @MarcosCasagrande Yes, it is working now. I just combined the 2 into the original catch, and have it not exit until the write fails or succeeds. – James Jun 22 '18 at 16:50
  • Yeah, that's what you need to do, handle process exit, ans wait until the log is written – Marcos Casagrande Jun 22 '18 at 16:51

2 Answers2

0

I found that I was actually catching uncaughtExceptions twice...

process
    .on('unhandledRejection', (reason, p) => {
        console.error(reason, 'Unhandled Rejection at Promise', p);
        console.log(reason, 'Unhandled Rejection at Promise', p);
        process.exit(1);
    })
    .on('uncaughtException', _err => {
        console.error(_err, 'Uncaught Exception thrown');
        console.log(_err, 'Uncaught Exception thrown');
        process.exit(1);
    });

This was causing the previous method to die before it could actually write the error to the file (due to process.exit(1)).

James
  • 3,765
  • 4
  • 48
  • 79
-1

what i can ask you to try is,

maybe you are catching uncaught Exceptions more than one time ,that could be the reason for previous method to die before actually writing error to the file.

it is not safe to resume normal operation after 'uncaughtException', because the system becomes corrupted. The correct use of 'uncaughtException' is to perform synchronous cleanup of allocated resources (e.g. file descriptors, handles, etc) before shutting down the process.

I strongly recommend you read this -> https://www.joyent.com/node-js/production/design/errors

can see this example

 var cluster = require('cluster');
 var numCPUs = require('os').cpus().length;
 if (cluster.isMaster) {
 for (var i = 0; i < numCPUs; ++i) {
 cluster.fork();
  }
cluster.on('exit', (worker, code, signal) => {
console.log('worker ${worker.process.pid} died');
cluster.fork();
});
} else {
var http = require('http');
var httpServer = http.createServer(app).listen(httpPort, function () {
console.log('process id local', process.pid)
console.log("http server started at port " + httpPort);
});
}
process.on('uncaughtException', function (err) {
console.error((new Date).toUTCString() + ' uncaughtException:', err.message)
console.error(err.stack)
process.exit(1)
})`
Rohit Jaiswal
  • 257
  • 4
  • 6