Error capture and handling
You can use node's built-in domain module for this .
Domains provide a way to handle several different I / O operations as a single group. If any of the emitters of events or callbacks registered to the domain emits an error event or throws an error, then the domain object will be notified and not lose the error context in the process.on ('uncaughtException') handler or in calling the program to exit immediately with code mistakes.
It is very important to note the following:
Domain error handlers are not a substitute for closing your process when an error occurs.
By the very nature of how throwing in JavaScript works, there has almost never been any way to safely “pick up where you left off” without leaking a link or creating some other fragile undefined state.
Since you are only asking how to respond with a 500 error, I am not going to deal with server processing, etc., as node documentation does; I highly recommend looking at an example in node docs . Their example shows how to fix the error, send the error response back to the client (if possible), and then restart the server. I will just show the creation of the domain and send a 500 response. (see next section on restarting the process)
Domains work similar to putting try / catch in your createServer . In your callback:
- Create a new domain object
- Listen to the event in the
error domain - Add
req and res to the domain (since they were created before the domain existed) run domain and call the request handler (this is similar to the try part of try / catch )
Something like that:
var domain = require('domain'); function handleRequest(req, res) {
Restarting the process after an error
Using the cluster module, you can successfully restart the process after an error. I basically copy the example from the node documentation here, but the general idea is to start several workflows from the main process. Workers are processes that process incoming connections. If one of them has a fatal error (i.e., those that we catch in the previous section), then it will disconnect from the main process, send a response of 500 and exit. When the master process sees that the workflow is shutting down, it will know that an error has occurred and a new worker is deploying. Since several workflows are running at the same time, there should be no problem with the absence of incoming connections if one of them does not work.
Sample code copied from here :
var cluster = require('cluster'); var PORT = +process.env.PORT || 1337; if (cluster.isMaster) { // In real life, you'd probably use more than just 2 workers, // and perhaps not put the master and worker in the same file. // // You can also of course get a bit fancier about logging, and // implement whatever custom logic you need to prevent DoS // attacks and other bad behavior. // // See the options in the cluster documentation. // // The important thing is that the master does very little, // increasing our resilience to unexpected errors. cluster.fork(); cluster.fork(); cluster.on('disconnect', function(worker) { console.error('disconnect!'); cluster.fork(); }); } else { // the worker // // This is where we put our bugs! var domain = require('domain'); // See the cluster documentation for more details about using // worker processes to serve requests. How it works, caveats, etc. var server = require('http').createServer(function(req, res) { var d = domain.create(); d.on('error', function(er) { console.error('error', er.stack); // Note: we're in dangerous territory! // By definition, something unexpected occurred, // which we probably didn't want. // Anything can happen now! Be very careful! try { // make sure we close down within 30 seconds var killtimer = setTimeout(function() { process.exit(1); }, 30000); // But don't keep the process open just for that! killtimer.unref(); // stop taking new requests. server.close(); // Let the master know we're dead. This will trigger a // 'disconnect' in the cluster master, and then it will fork // a new worker. cluster.worker.disconnect(); // try to send an error to the request that triggered the problem res.statusCode = 500; res.setHeader('content-type', 'text/plain'); res.end('Oops, there was a problem!\n'); } catch (er2) { // oh well, not much we can do at this point. console.error('Error sending 500!', er2.stack); } }); // Because req and res were created before this domain existed, // we need to explicitly add them. // See the explanation of implicit vs explicit binding below. d.add(req); d.add(res); // Now run the handler function in the domain. d.run(function() { handleRequest(req, res); }); }); server.listen(PORT); } // This part isn't important. Just an example routing thing. // You'd put your fancy application logic here. function handleRequest(req, res) { switch(req.url) { case '/error': // We do some async stuff, and then... setTimeout(function() { // Whoops! flerb.bark(); }); break; default: res.end('ok'); } }
Note. . I still want to emphasize that you should take a look at the documentation for the domain module and look at examples and explanations there. This explains most, if not all, of this, the arguments behind it, and other situations that you might encounter.