A Complete Guide to Logging in Node.js with Winston and AWS CloudWatch

A Complete Guide to Logging in Node.js with Winston and AWS CloudWatch

Logging is a crucial part of any application development. It helps track application behavior, debug issues, monitor performance, and ensure everything works as expected.

What is Winston?

Winston is a logging library for Node.js designed to be simple, flexible, and extensible. It supports multiple logging transports, which means you can send your logs to different destinations such as a file, the console, or even cloud services like AWS CloudWatch.

Setting Up Winston in Node.js

Step 1: Installing Winston

Start by installing Winston in your Node.js project:

npm install winston

Step 2: Basic Setup for Winston

Create a new file logger.js and add the following code to set up Winston:

const winston = require('winston');

// Create a Winston logger instance
const logger = winston.createLogger({
  level: 'info', // Default log level
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.json() // Logs will be in JSON format
  ),
  transports: [
    new winston.transports.File({ filename: 'logs/error.log', level: 'error' }),  // Log errors to error.log
    new winston.transports.File({ filename: 'logs/combined.log' })                // Log all messages to combined.log
  ],
});

// If we're in development, log to the console
if (process.env.NODE_ENV !== 'production') {
  logger.add(new winston.transports.Console({
    format: winston.format.simple(), // Simple output in the console for readability
  }));
}

module.exports = logger;

In this setup:

  • We log all messages to combined.log.

  • Errors are logged separately to error.log.

  • For development environments, logs are also sent to the console.

Step 3: Using Winston in Your Application

You can now import and use your logger throughout your Node.js app:

const logger = require('./logger');

logger.info('Server started successfully');
logger.error('Error occurred during server initialization');

Adding Log Rotation with Daily Log Files

Winston has a useful plugin called winston-daily-rotate-file that allows you to automatically rotate logs daily.

Step 1: Install the Daily Rotate File Transport

npm install winston-daily-rotate-file

Step 2: Set Up Log Rotation

Modify your logger.js to include log rotation:

const winston = require('winston');
const DailyRotateFile = require('winston-daily-rotate-file');

// Configure Winston with Daily Rotate File transport
const logger = winston.createLogger({
  level: 'info',
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.json()
  ),
  transports: [
    new DailyRotateFile({
      filename: 'logs/application-%DATE%.log',
      datePattern: 'YYYY-MM-DD',
      maxFiles: '14d', // Keep logs for 14 days
    }),
  ],
});

This setup creates new log files daily, and older logs are automatically deleted after 14 days.


Adding Security Headers with Helmet

Security is another important aspect, especially for web applications. Helmet is a middleware that helps secure your Express app by setting HTTP headers.

Step 1: Install Helmet

npm install helmet

Step 2: Use Helmet in Your Application

Add Helmet to your Express app to automatically apply security headers:

const express = require('express');
const helmet = require('helmet');
const app = express();

// Use Helmet for security
app.use(helmet());

app.get('/', (req, res) => {
  res.send('Hello World with Security Headers!');
});

app.listen(3000, () => {
  console.log('Server is running on port 3000');
});

Uploading Logs to AWS CloudWatch

To manage and monitor logs from various servers or instances, you can use AWS CloudWatch. We'll use the WinstonCloudWatch transport to send logs directly to CloudWatch.

Step 1: Install Winston CloudWatch

npm install winston-cloudwatch aws-sdk

Step 2: Configure Winston with CloudWatch

Modify your logger.js to include AWS CloudWatch as a log transport:

const winston = require('winston');
const WinstonCloudWatch = require('winston-cloudwatch');

// Configure AWS credentials (optional if using IAM roles)
const AWS = require('aws-sdk');
AWS.config.update({ region: 'us-east-1' });

// Configure Winston logger with CloudWatch transport
const logger = winston.createLogger({
  transports: [
    new WinstonCloudWatch({
      logGroupName: 'node-app-logs',    // CloudWatch log group name
      logStreamName: 'node-app-stream', // CloudWatch log stream name
      awsRegion: 'us-east-1',
    }),
  ],
});

Step 3: Using the Logger

Now, whenever you use the logger, it will send logs to AWS CloudWatch:

logger.info('This is an info message');
logger.error('This is an error message');

Step 4: View Logs in CloudWatch

You can view and analyze the logs in the AWS CloudWatch Console by navigating to Log Groups and selecting your logGroupName.

Automating Log Deletion with Cron Jobs

While Winston rotates and manages logs automatically, you may still want to run custom cron jobs to delete old logs or manage log retention.

Step 1: Install Node Cron

npm install node-cron

Step 2: Create a Cron Job for Log Deletion

In logger.js, you can set up a cron job to run every day and delete log files older than 14 days:

const cron = require('node-cron');
const fs = require('fs');
const path = require('path');

const logDirectory = path.join(__dirname, 'logs');

// Set up a cron job to delete old logs every day at midnight
cron.schedule('0 0 * * *', () => {
  fs.readdir(logDirectory, (err, files) => {
    if (err) throw err;

    files.forEach((file) => {
      const filePath = path.join(logDirectory, file);
      fs.stat(filePath, (err, stats) => {
        if (err) throw err;

        const now = Date.now();
        const fileAge = now - stats.mtimeMs;

        if (fileAge > 14 * 24 * 60 * 60 * 1000) { // 14 days in ms
          fs.unlink(filePath, (err) => {
            if (err) throw err;
            console.log(`Deleted old log file: ${file}`);
          });
        }
      });
    });
  });
});

Conclusion

By combining Winston for logging, Helmet for security, and AWS CloudWatch for centralized log management, you create a robust, scalable, and secure logging infrastructure for your Node.js application. This setup ensures that logs are handled efficiently, with automatic rotation, cloud-based storage, and optional cron jobs for custom log management.

By integrating these tools, your application becomes easier to monitor, debug, and scale in production environments.