javascript

How Can You Master Log Management in Express.js With Morgan and Rotating File Streams?

Organized Chaos: Streamlining Express.js Logging with Morgan and Rotating-File-Stream

How Can You Master Log Management in Express.js With Morgan and Rotating File Streams?

Logging in a web application built with Express.js is super important for keeping tabs on what’s happening and fixing issues when they pop up. One great way to manage your logs is by combining morgan middleware and rotating-file-stream. This duo helps you store logs in rotating files so they stay organized and don’t grow endlessly.

First off, you’ll need to install both morgan and rotating-file-stream using npm. It’s as simple as running:

npm install morgan rotating-file-stream

Once you’ve got them installed, you can set up your Express app to use these modules.

Here’s a basic setup for logging requests into a file that rotates daily:

const express = require('express');
const morgan = require('morgan');
const rfs = require("rotating-file-stream");

const app = express();
const port = 3000;

// Create a rotating write stream
const accessLogStream = rfs.createStream("access.log", {
  interval: "1d", // rotate daily
  path: __dirname + "/log"
});

// Setup the logger
app.use(morgan("combined", { stream: accessLogStream }));

app.get('/', (req, res) => {
  res.send('hello, world!');
});

app.listen(port, () => {
  console.debug(`App listening on :${port}`);
});

In this example, rotating-file-stream creates a new log file every day, and morgan logs all requests in the Apache combined format to this rotating log stream.

If you want to customize your log format and add more details like the user’s IP or a unique request ID, you can do it this way:

const express = require('express');
const morgan = require('morgan');
const rfs = require("rotating-file-stream");
const uuid = require('node-uuid');

const app = express();
const port = 3000;

// Create a rotating write stream
const accessLogStream = rfs.createStream("access.log", {
  interval: "1d", // rotate daily
  path: __dirname + "/log"
});

// Define custom tokens
morgan.token('id', req => req.id);
morgan.token('ip', req => req.headers['x-forwarded-for'] || req.connection.remoteAddress);

// Assign a unique ID to each request
app.use((req, res, next) => {
  req.id = uuid.v4();
  next();
});

// Setup the logger with custom format
app.use(morgan(':id :ip ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"', { stream: accessLogStream }));

app.get('/', (req, res) => {
  res.send('hello, world!');
});

app.listen(port, () => {
  console.debug(`App listening on :${port}`);
});

In this setup, every log entry includes a unique request ID and the user’s IP address.

Sometimes you might need to rotate logs not just daily, but also based on size. This makes sure no single log file gets too big:

const express = require('express');
const morgan = require('morgan');
const rfs = require("rotating-file-stream");

const app = express();
const port = 3000;

// Create a rotating write stream based on size
const accessLogStream = rfs.createStream("access.log", {
  size: "10M", // rotate every 10MB written
  interval: "1d", // rotate daily
  compress: "gzip" // compress rotated files
});

// Setup the logger
app.use(morgan("combined", { stream: accessLogStream }));

app.get('/', (req, res) => {
  res.send('hello, world!');
});

app.listen(port, () => {
  console.debug(`App listening on :${port}`);
});

This way, logs will rotate daily and whenever the file size exceeds 10MB.

You can also make your logging setup more flexible using environment variables:

require('dotenv').config(); // load variables from .env file

const express = require('express');
const morgan = require('morgan');
const rfs = require("rotating-file-stream");

const app = express();
const port = 3000;

// Create a rotating write stream
const rfsStream = rfs.createStream(process.env.LOG_FILE || 'access.log', {
  size: process.env.LOG_SIZE || '10M',
  interval: process.env.LOG_INTERVAL || '1d',
  compress: 'gzip' // compress rotated files
});

// Setup the logger
const format = process.env.LOG_FORMAT || "dev";
app.use(morgan(format, { stream: rfsStream }));
app.use(morgan(format));

app.get('/', (req, res) => {
  res.send('hello, world!');
});

app.listen(port, () => {
  console.debug(`App listening on :${port}`);
});

This setup lets you control log file names, sizes, intervals, and formats using environment variables defined in a .env file.

Another handy feature is logging errors and successes separately. This way, you can easily track down issues without sifting through all other logs:

const express = require('express');
const morgan = require('morgan');
const rfs = require("rotating-file-stream");

const app = express();
const port = 3000;

// Create rotating write streams
const errorLogStream = rfs.createStream("error.log", {
  interval: "1d", // rotate daily
  path: __dirname + "/log"
});

const successLogStream = rfs.createStream("success.log", {
  interval: "1d", // rotate daily
  path: __dirname + "/log"
});

// Skip logic for error and success logs
const skipSuccess = (req, res) => res.statusCode < 400;
const skipError = (req, res) => res.statusCode >= 400;

// Setup the logger for errors
app.use(morgan("combined", { skip: skipSuccess, stream: errorLogStream }));

// Setup the logger for successes
app.use(morgan("combined", { skip: skipError, stream: successLogStream }));

app.get('/', (req, res) => {
  res.send('hello, world!');
});

app.listen(port, () => {
  console.debug(`App listening on :${port}`);
});

In this config, error responses (4xx and 5xx status codes) go into one file and successful responses into another.

At times, you might run into issues where log rotation doesn’t work as expected. A common fix for daily rotation issues is to ensure the file name generator is set up right:

const express = require('express');
const morgan = require('morgan');
const rfs = require("rotating-file-stream");
const path = require('path');
const { format } = require('date-fns');

const app = express();
const port = 3000;

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

const accessLogStream = rfs.createStream(logFilename(), {
  interval: "1d", // rotate daily
  path: path.resolve(__dirname, '..', 'log'),
});

// Setup the logger
app.use(morgan(':method :url :status :res[content-length] ":referrer" ":user-agent"', { stream: accessLogStream }));

app.get('/', (req, res) => {
  res.send('hello, world!');
});

app.listen(port, () => {
  console.debug(`App listening on :${port}`);
});

Here, the logFilename function makes sure the log file name includes the current date, which helps in daily rotation.

Combining morgan with rotating-file-stream provides a solid logging solution for your Express.js apps. You can customize your log formats, rotate logs by size or time, and even use environment variables to keep everything in check. This keeps your logs well-organized and easier to analyze, making debugging and monitoring a breeze.

Keywords: logging, Express.js, morgan middleware, rotating-file-stream, npm install, log rotation, daily logs, custom log format, environment variables, Express logging setup



Similar Posts
Blog Image
Temporal API: JavaScript's Game-Changer for Dates and Times

The Temporal API is a new proposal for JavaScript that aims to improve date and time handling. It introduces intuitive types like PlainDateTime and ZonedDateTime, simplifies time zone management, and offers better support for different calendar systems. Temporal also enhances date arithmetic, making complex operations easier. While still a proposal, it promises to revolutionize time-related functionality in JavaScript applications.

Blog Image
Unlock Full-Stack Magic: Build Epic Apps with Node.js, React, and Next.js

Next.js combines Node.js and React for full-stack development with server-side rendering. It simplifies routing, API creation, and deployment, making it powerful for building modern web applications.

Blog Image
Mocking Browser APIs in Jest: Advanced Techniques for Real-World Testing

Mocking browser APIs in Jest simulates browser behavior for testing. Techniques include mocking window object, DOM interactions, asynchronous operations, and modules. Use simple mocks, reset between tests, and handle edge cases for robust testing.

Blog Image
**JavaScript Memory Management: 7 Pro Techniques to Prevent Leaks and Boost Performance**

Optimize JavaScript memory management with proven techniques: eliminate leaks, leverage garbage collection, manage event listeners & closures for peak app performance.

Blog Image
Modular Architecture in Angular: Best Practices for Large Projects!

Angular's modular architecture breaks apps into reusable, self-contained modules. It improves maintainability, reusability, and scalability. Implement with NgModules, feature modules, and lazy loading for better organization and performance.

Blog Image
Unlock Real-Time Magic: Build Collaborative Apps with React and Firebase

React and Firebase enable real-time collaborative apps. Users work together seamlessly, creating interactive experiences. Combine React's UI capabilities with Firebase's real-time database for powerful, engaging applications. Authentication and chat features enhance collaboration.