Skip to content

Overview

This documentation explains how to implement and configure Morgan, a Node.js middleware for logging HTTP requests and responses. Morgan captures specific data points about requests and responses, making it valuable for tracking, debugging, and monitoring application activity.

Installation

Morgan is available as a Node.js module and can be installed through the npm registry with the following command:

bash
npm install morgan

Usage

To integrate Morgan into your Node.js application, create a custom middleware that leverages both built-in and custom tokens to capture essential data for each request and response.

Setting Up Middleware

  1. Create a Middleware File Create a file named morgan.js inside the src/middleware directory. This file will be used to log API requests and responses with Morgan.

  2. Define Commonly Used Tokens

Morgan provides several built-in tokens that you can use to capture common request and response details.

  • :method - HTTP method (e.g., GET, POST).
  • :url - URL path of the request.
  • :status - HTTP status code of the response.
  • :response-time - Time taken to process the request, in milliseconds.
  • :remote-addr - IP address of the request origin.
  • :user-agent - User-agent header (identifies client software).
  1. Example of Predefined Token Format

Here’s an example of a Morgan format string using predefined tokens:

javascript
app.use(morgan(':method :url :status :response-time ms - :input :requester'));
  1. Create Custom Tokens

Custom tokens allow you to log additional, application-specific data. You can define these in the morgan.js middleware file.

morgan.js Middleware Implementation in file src/middleware/morgan.js

The morgan.js file contains custom tokens to log detailed request and response data, including masking sensitive information.

javascript
let morgan = require("morgan");

morgan.token("requester", function getRequester(req) {
  return JSON.stringify(req.requester);
});
morgan.token("input", function getInput(req) {
  let input = {};
  if (req.method == "GET") {
    input = req.query;
  } else {
    input = req.body;
  }
  
  // mask any input that should be secret
  input = { ...input };
  if (input.password) {
    input.password = "*";
  }

  return JSON.stringify(input);
});

morgan.token("response-body", function getResponseBody(req, res) {
  const body = { ...JSON.parse(res.responseBody) };
  // mask any input that should be secret
  if (body?.data?.accessToken) {
    body.data.accessToken = "*";
  }
  if (body?.data?.refreshToken) {
    body.data.refreshToken = "*";
  }
  
  return JSON.stringify(body);
});
module.exports = morgan;

Log Routing and Rotation

  1. Configure Log Rotation using rotating-file-stream to archive logs daily and maintain a set file size limit.
  • File Naming: Use the current date for daily log files.
  • Rotation Interval: Rotate daily.
  • Compression: Enable gzip compression for rotated files.
  • Location: Store logs in the logs folder.
  1. Log Rotation Configuration in src/routes/api/index.js:
javascript
const fs = require('fs');
const path = require('path');
let rfs = require('rotating-file-stream');
const { format } = require('date-fns');

// Helper function to generate log filenames
function logFilename() {
    return `${format(new Date(), 'yyyy-MM-dd')}-access.log`;
}

// Create a write stream with log rotation
let accessLogStream = rfs.createStream(logFilename, {
  interval: '1d',   // Rotate daily
  path: path.resolve(__dirname, '../../logs'),  // Log directory
  size: '10M',      // Rotate every 10MB
  compress: 'gzip'  // Compress rotated logs
});
  1. Capture and Modify the Response Body in file src/routes/api/index.js
javascript
const originalSend = express.response.send;
express.response.send = function sendOverride(body) {
  this.responseBody = body; // Store response body
  return originalSend.call(this, body); // Send original response
};
  1. Apply Morgan with Custom Tokens in file src/routes/api/index.js

Finally, integrate the custom tokens and write logs using the accessLogStream:

javascript
router.use(
    morgan(
      ':requester :remote-addr [:date[clf]] ":method :url HTTP/:http-version" Request :input Response :response-body :response-time', 
      { stream: accessLogStream }
    )
  );

Summary

This Morgan setup captures request and response details with flexible logging options and log rotation. Custom tokens allow masking of sensitive information, making this a secure and robust logging solution for your Node.js application.

References