Use process.on('uncaughtException to show a 500 error page - node.js

I'm developing an express app.
I currently have the following in my server.js
process.on('uncaughtException', function (err) {
console.log( "UNCAUGHT EXCEPTION " );
console.log( "[Inside 'uncaughtException' event] " + err.stack || err.message );
});
Which stops the server crashing every time there's an error, however, it just sits there...
is it possible to instead of console.log, to send a 500 error page with the error details?

I don't think you can from within the uncaughtException do a response since that could happen even when there is no request occurring.
Express itself provides a way to handle errors within routes, like so:
app.error(function(err, req, res, next){
//check error information and respond accordingly
});

Per ExpressJS Error Handling, add app.use(function(err, req, res, next){ // your logic }); below your other app.use statements.
Example:
app.use(function(err, req, res, next){
console.log(err.stack);
// additional logic, like emailing OPS staff w/ stack trace
});

Related

Prevent stack trace leak in production in Nodejs application

I was looking at this Nodejs express Guthub repo. The production strack trace leak prevention is done using:
// development error handler
// will print stacktrace
if (!isProduction) {
app.use(function(err, req, res, next) {
console.log(err.stack);
res.status(err.status || 500);
res.json({'errors': {
message: err.message,
error: err
}});
});
}
// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.json({'errors': {
message: err.message,
error: {}
}});
});
I do not understand why the production strack trace middleware does not have to be inside the else condition
In the example you shared, an else block would probably make things a bit more clear, but it's not strictly needed. If isProduction is truthy, the error handling middleware inside the if block will handle the error. Since it does not call next(err); to pass the request to the next middleware, control will not be passed to the following "production" error-handling middleware. The response is sent and Express is done handling this particular request.
If isProduction is falsey, obviously the "development" middleware is not ever registered with Express and all errors will be handled in the "production" error-handler.
The above example, however, would break down if you needed to add another error-handling middleware after the "production" error-handler or something.
More Details
In Express, middlewares are run sequentially in the order in which they were registered and control is passed to the next middleware by using the calling the next parameter. See the documentation here.
Error handling middleware, like the ones posted in your example, work the same way. The error will reach these middlewares because a previous middleware called next and passed an error like so: next(new Error('Something went wrong));.

Supress logging to console on next(err) method in express

app.get('/test', (req, res, next) => {
const err = new Error('Test');
next(err);
});
express will log the error and stacktrace to the console. Is there a way that I can suppress the logging?
If you put an error handler middleware in your Express implementation to handle the next(err) call like this:
// defined as the last route
app.use(function (err, req, res, next) {
res.status(500).send('Something broke!')
});
then, Express won't log any error and you can control what response is sent for the error.
I'd suggest reading this page on Express error handling: https://expressjs.com/en/guide/error-handling.html.
If you look in the express code where this logging comes from, it comes from this code:
function logerror(err) {
/* istanbul ignore next */
if (this.get('env') !== 'test') console.error(err.stack || err.toString());
}
which curiously enough shows that if you do this:
app.set('env', 'test');
or you set NODE_ENV=test in the environment before launching your server, then it will skip the error logging too. But, it's much better to just control things with your own error handler as I show about where you can also control what type of response is sent.

Express middleware error handler

Following a generated template for express. In the app.js there is a following snippet
app.use('/users', users);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
Per my understanding, the middleware will run in order from app.use('/users', users) to 404 handler to error handler. My question is how will it be possible to reach error handler and execute this line res.status(err.status || 500); ? Wont every failed request be passed through 404 handler first and therefor getting the status code of 404? Please let me know if I am missing something! Thank you
No, it won't. If you look at these event handlers declarations you'll see that error handler for unhandled error, has an additional err parameter:
app.use(function(req, res, next) {
app.use(function(err, req, res, next) {
Error-handling middleware always takes four arguments. You must provide four arguments to identify it as an error-handling middleware function. Even if you don’t need to use the next object, you must specify it to maintain the signature. Otherwise, the next object will be interpreted as regular middleware and will fail to handle errors. For details about error-handling middleware.
So, when the route is not found, the last declared middleware is calling, it's 404 error handler.
But when you call next with error: next(err) or your code throws an error, the last error handler is calling.
System error should be handled before 404
app.use('/users', users);
// error handler
app.use(function(err, req, res, next) {
// No routes handled the request and no system error, that means 404 issue.
// Forward to next middleware to handle it.
if (!err) return next();
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
// catch 404. 404 should be consider as a default behavior, not a system error.
app.use(function(req, res, next) {
res.status(404);
res.render('Not Found');
});
Wont every failed request be passed through 404 handler first and therefor getting the status code of 404?
No, the 404 route is just a standard middleware and usually wired up last, meaning if no other routes handle the request they will eventually hit the 404.
The 500 is a special type of middleware, you'll notice it has 4 parameters (first one being an error param). This middleware is invoked as soon as you call next with an error, or any sort of data for that matter.
See the docs
Something to note for anyone else that stumbles around this post.
Your last middleware to be invoked (if none gets invoked) - will be 404, it doesn't take an err as its middleware parameter. Hence, you can't/shouldn't try to catch the 404 before any other middleware. So your last middleware should be without the err parameter like the example below and of course, should be at the bottom of your application stack.
//at the botton of your app.js
// catch 404.
//no err parameter
app.use(function(req, res, next) {
res.render("404Page"{
url: req.url
});
});
If you want to catch your errors before it goes to this handler use a (err, req, res, next) before this middleware.

Express 4: Handle every and all errors

While coding my app, I sometimes had a little mishap when typing my URLs in the browser, and thus sometimes got the error message:
Cannot GET /some/route
Which was true, since the route may was not defined.
But since this app is planned to enter production, I kinda don't want to use this flat message as my "error page".
Looking into the Express 4 docs, they tell me to .use() a middleware with 4 arguments. I did that. But I'd still get this issue...
Turns out that this message comes from the finalhandler module and my bet is, that this middleware comes before my error-catching, 4-argument middleware.
Here is a basic express app that I threw together while trying to find a solution:
var app = require("express")();
app.use("/yo", function(req, res, next){
res.send("Yo!");
});
app.use(function(error, req, res, next){
res.send("An error: "+error);
console.log(error);
});
app.listen(10000);
Accessing /yo works. But, / or /derp yields the Cannot GET message instead of my little middleware.
So, how is this done correctly, now?
The error middleware is only for actual errors, such as a middleware or route handler throwing an exception or passing an error to next().
If you want to provide a route handler for requests that do not match any existing routes, then just add a middleware after all of your app's routes/middleware like:
var app = require("express")();
app.use("/yo", function(req, res, next){
res.send("Yo!");
});
app.use(function(req, res, next) {
res.send('Could not route your request!');
});
app.use(function(error, req, res, next){
res.send("An error: "+error);
console.log(error);
});
app.listen(10000);

Automatically log HTTP 500 responses in Express?

A common pattern in my Express app is to include the following in all my routes:
//...code which could result in an err
if (!err) return res.send(200);
console.log(err); // Would prefer to omit this line
res.send(500);
Right now, I need to write console.log(err) in all my routes. I'd prefer to just automatically log the err variable every time a 500 is sent. Is there any way to hook into Express and automatically log the call stack and/or err for all 500 responses?
From your explanation, you seem to be including error handling in all your routes.
You probably should create a global interceptor error handler which can perform the logging for you and still be the base error handler if you have a 500 error type.
//if your express var is app
var errorHandler = function(err, req, res, next){
console.log(err.stack);
res.send(500);
// or you could call res.render('error'); if you have a view for that.
};
app.use(app.router); // this has to come before your interceptors
app.use(notFound); // your page not found interceptor
app.use(errorHandler);

Resources