51

I have a question: how do I can handle all uncaught Exception (operations/developer error will take all service down) for my node app. Then I can send an email alert to me whenever catch an error.

Nam Tran Thanh
  • 821
  • 2
  • 8
  • 14

3 Answers3

101

You can use process 'uncaughtException' and 'unhandledRejection' events.

Also remember that 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.

Example:

process
  .on('unhandledRejection', (reason, p) => {
    console.error(reason, 'Unhandled Rejection at Promise', p);
  })
  .on('uncaughtException', err => {
    console.error(err, 'Uncaught Exception thrown');
    process.exit(1);
  });
Dario
  • 3,905
  • 2
  • 13
  • 27
  • 2
    Can, but shouldn't. See @xShirase's answer. – Paul Nov 29 '16 at 14:08
  • 1
    You are right Paul. For this, in the example I suggest to log the error and stop de program (with exit code !== 0) to avoid an an undefined state of the application. – Dario Nov 29 '16 at 14:11
  • 1
    Yep, I +1'd you for using the right technique *if* you're going to go the uncaughtException route, I just wanted to amplify that it's not the best route, and discourage the OP from thinking he should do what you have and then embed some kind of email sender in the event listener. – Paul Nov 29 '16 at 14:15
  • I added the note. Thank you – Dario Nov 29 '16 at 14:23
  • This is the route I want to take because I want to save the state of the application before it terminates. I can't think of any possible reason I "shouldn't" do this, as saving the state will allow to me skip over a bunch of work once I fix whatever error caused the exception. – Michael Sep 24 '22 at 21:19
21

You could use the Domain API : https://nodejs.org/api/domain.html But it is deprecated and not recommended anyway.

In general, you want to handle errors anywhere they may occur and avoid a "catch all" approach.

If an error occurs, ignoring it is no incentive to fix it, and in some cases could lead to you not even knowing that your program is malfunctioning.

Instead, the best way to handle it is to have your program crash, log the crash (and the stack/core dump), then restart it automatically, using pm2 or nodemon.

For a (very) long but insightful speech by Joyent (Node creators), I strongly recommend you read this link : Error handling in Node.JS

There's also a process.on('uncaughtException') event (that you should also not use)

Edit: A bit more details and an attempt at solving your issue. Using a software like pm2 to restart your app on crash, you will also be able to see the error.log, which will give you the stack trace. So it seems that the only thing you still need is to be alerted of the crash.

For that, you may want to have a look at interfaces like keymetrics (same guys who made pm2) which potentially could alert you on errors.

A cool solution I used once, a long long time ago was as follows :

  • when your app (re)starts, it looks for an error log
  • If it finds one, it alerts you with the content of the log file
  • it then renames/move the error logfile to a different location

I wouldn't necessary recommend this solution, but it fits all the specs you need, so have fun with it!

Edit2 : If you feel like delving deeper into the topics of service development and best practices, have a look at his link suggested by @Paul in the comments : https://12factor.net/

xShirase
  • 11,975
  • 4
  • 53
  • 85
  • +1 for the link to the Joyent article. Also, note particularly the section titled (Not) Handling Programmer Errors, which I think gets to the meat of the OP's question. – Paul Nov 29 '16 at 14:05
  • 2
    Also worth noting that node follows the unix principle, meaning that each application should do one thing well. In the case of wanting an email when the app crashes, that's outside the scope of your app. Instead, you crash (and log) as @xShirase says, and then have something else (e.g. systemd or logwatch) taking note of that and sending emails when a threshold is reached (even if that threshold is "any error"). – Paul Nov 29 '16 at 14:11
  • just added a hack I used when I was still young and careless to do just that within the app itself :) +1 for the unix principle though – xShirase Nov 29 '16 at 14:14
  • I think there's like a set of 'stages of enlightenment' that Node devs seem to all go through (at least, all the ones I've known/observed have, myself included), where you start off with this enterprisey-best-practice background of trying to package everything a certain way and only later learn to let go and trust the environment and supporting tools. 12factor.net is a good resource to help with that, but a lot of it is learning what tools are available. I've only recently started using things like systemd and logstash to do what i used to do with pm2 and custom code. No new wheels. :) – Paul Nov 29 '16 at 14:18
  • 1
    Mastering Unix is mastering a whole lot of tools. I've been through the same stages, only to realize that I knew how to do all those tasks, using tried and tested unix tools, which run faster, and often better than a patched Node solution. I like looking at my old code, so full of unnecessary things :) – xShirase Nov 29 '16 at 14:23
  • so, what I need to do is don't catch them, use logwatch to store log and send mail to me. I will review log file. I have a question: how to setting logwatch only send email when have an error? Can or cannot? – Nam Tran Thanh Nov 30 '16 at 12:33
  • Can, of course :) Check out 'Configuring LogWatch' in this link : https://www.digitalocean.com/community/tutorials/how-to-install-and-use-logwatch-log-analyzer-and-reporter-on-a-vps – xShirase Nov 30 '16 at 12:35
  • Archive.org for the Joyent article: https://web.archive.org/web/20220128053456/https://www.joyent.com/node-js/production/design/errors I can't find an official-looking new home for it. – Rup Jul 18 '23 at 15:50
9

the best way is to let the application crash, log the error and then restart the process. you can do it simply like this

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)
})
vaughan
  • 6,982
  • 6
  • 47
  • 63
Suman
  • 373
  • 4
  • 9