Node Logging Like A Boss: Using Winston And AWS CloudWatch

Alexander Paterson
|
Posted about 6 years ago
|
4 minutes
Node Logging Like A Boss: Using Winston And AWS CloudWatch
Essential for backend developers: log everything to a simple dashboard with AWS Cloudwatch

This is going to give your applications an extra level of awesome: advanced logging.

Application showing an unidentified error? Tired of ssh'ing into your server to debug it? Instead of cosole.log and console.err, you should be using a logger like winston to send your logs somewhere more convenient. Here, I'll show you how to send them to AWS CloudWatch for easy access and monitoring. You can even set up CloudWatch to notify you by email of certain errors.

The Logger

We're going to need two node modules: winston, and winston-aws-cloudwatch. With these installed, you can use the following code as a basic implementation of our logger:

var winston = require('winston'),
    CloudWatchTransport = require('winston-aws-cloudwatch');

var NODE_ENV = process.env.NODE_ENV || 'development';

const logger = new winston.Logger({
  transports: [
    new (winston.transports.Console)({
      timestamp: true,
      colorize: true,
    })
  ]
});

var config = {
  logGroupName: 'my-log-group',
  logStreamName: NODE_ENV,
  createLogGroup: false,
  createLogStream: true,
  awsConfig: {
    accessKeyId: process.env.CLOUDWATCH_ACCESS_KEY_ID,
    secretAccessKey: process.env.CLOUDWATCH_SECRET_ACCESS_KEY,
    region: process.env.CLOUDWATCH_REGION
  },
  formatLog: function (item) {
    return item.level + ': ' + item.message + ' ' + JSON.stringify(item.meta)
  }
}

if (NODE_ENV != 'development') logger.add(CloudWatchTransport, config);

logger.level = process.env.LOG_LEVEL || "silly";

logger.stream = {
  write: function(message, encoding) {
    logger.info(message);
  }
};

module.exports = logger;

Up the top we create a Logger with a single transport, then we add another transport, but only if we're not in development. You might prefer to only add the logger if you are in production/staging. Look here for more information about winston's log levels.

CloudWatch

Clearly we need to set up CloudWatch. We need to create a log group, and a programmatic IAM with the correct permissions.

On the AWS console, under CloudWatch > Logs create a log group called my-log-group like above (or whatever):

CloudWatch Create Log Group

Now for the IAM user. Navigate to the IAM users dashboard and hit Add User:

IAM User Dashboard

Give the user a meaningful name (maybe cloudwatch-full-access), and an Access Type of Programmatic Access.

Attach an existing policy: CloudWatchFullAccess and create the user. You should ideally figure out how to edit the policy to only allow this user access to the specific log group.

Create the user, and store its secret keys in a secure environment file. With this information, you can now define all the environment variables our logger is relying on, and it is now usable!

Usage

var logger = require('../services/logger'),

logger.log('info', "[STARTUP] Connecting to DB...", {tags: 'startup,mongo'});
// -> Connect to DB

It's that easy. You should now be able to see any logs under the log group on your CloudWatch dashboard. If you want to query for a specific tag, use:

{$.tags = "*tag*"}


-->
ALEXANDER
PATERSON