Categories
Node.js Best Practices

Node.js Best Practices — Logging and Monitoring

Spread the love

Like any kind of apps, JavaScript apps also have to be written well.

Otherwise, we run into all kinds of issues later on.

In this article, we’ll look at some best practices we should follow when writing Node apps.

Handle Errors Centrally

We should handle errors centrally in our Node app.

For example, we can call next in one middleware to call the error handling middleware that’s added after it.

We can write:

try {
  User.addNew(req.body).then((result) => {
    res.status(200).json(result);
  }).catch((error) => {
    next(error)
  });
} catch (error) {
  next(error);
}

to catch the error in the promise chain with the catch method.

And with the try-catch block, we catch errors in the synchronous code.

Then we can add an error handler middleware after that code by writing:

app.use((err, req, res, next) => {
  errorHandler.handleError(err).then((isOperationalError) => {
    if (!isOperationalError)
      next(err);
  });
});

We invoke another promise chain to handle any operational errors.

These are errors that we can anticipate and handle gracefully.

Document API Errors Using Swagger

API errors should be documented in some way so they can be handled.

We don’t want our app to crash.

Swagger lets us make document our APIs in an automated way.

If we don’t handle errors, then the app will crash.

Shut the Process Gracefully When Unknown Errors are Encountered

If our app encounters some errors that aren’t handled, then we should shut our app down gracefully and restart it.

A common way to do this is to use a process manager like PM2 to do this.

We don’t want any error to crash our app and bring it down.

If we don’t have a process manager, then we’ll have to restart it ourselves every time.

This is definitely not acceptable since it can happen many times a day at any time.

We can create our own error handler by writing:

function errorHandler() {
  this.handleError = function(error) {
    return logger.logError(err));
  }

  this.isTrustedError = function(error) {
    return error.isOperational;
  }
}

We have an error handler constructor with some methods to handle errors.

Use a Mature Logger

If a Node app it’s in production, then the errors won’t be visible unless we log them somewhere.

There’re many logging solutions available for Node apps, including Winston, Bunyan, and Log4js.

They’ll help us find errors faster.

We shouldn’t use crude solutions like console.log for logging errors in production.

The errors should be available in some cloud services or files that we can search.

For instance, we can use Winston by writing:

const logger = new winston.Logger({
  level: 'info',
  transports: [
    new (winston.transports.Console)(),
    new (winston.transports.File)({ filename: 'somefile.log' })
  ]
});

logger.log('info', 'log: %s', 'something', { anything: 'This is metadata' });

We use the winston.Logger constructor to create the logger.

Then we call log to log what we want in the format we want.

Discover Errors and Downtime Using APM Products

Errors and downtime should be recorded with APM products.

This way, we can find them easily all in one place.

They provide features like website or API monitoring.

Each request is recorded with the performance metrics of them.

If there are slow code, it’ll record where the slowness occurred.

Also, they’ll aggregate the data and then show them together in one dashboared.

Conclusion

Errors should be handled centrally.

Also, logging and monitoring should be done with various libraries and services.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *