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 ​