Logging Middleware

Logging is a critical part of any web application, as it helps developers track, debug, and understand the flow of an application. Effective logging allows you to capture information about incoming requests, responses, errors, and other significant events in your Express.js application. This chapter will provide an in-depth look at logging middleware in Express.js, covering everything from basic to advanced techniques. We will explore how to implement custom logging, integrate popular logging libraries, and follow best practices to ensure your logging system is robust and scalable.

What is Logging?

Logging involves recording information about the operation of your application. This can include details about incoming HTTP requests, responses sent to clients, errors encountered, and other significant events. Logs are essential for monitoring, debugging, and auditing an application.

Why Logging is Important ?

  • Debugging: Logs help identify and troubleshoot issues in the application.
  • Monitoring: Logs provide insight into the application’s performance, helping you detect anomalies or inefficiencies.
  • Auditing: Logs can be used to track user activities, important for compliance and security.
  • Error Handling: Logs allow you to capture and analyze errors, leading to more stable and reliable applications.

Types of Logs

  • Request Logs: Information about incoming requests, such as the method, URL, headers, and body.
  • Response Logs: Information about the responses sent to clients, including status codes and response times.
  • Error Logs: Detailed information about errors that occur during request processing.
  • Custom Logs: Any other relevant information that you wish to capture, such as user actions or specific application events.

Setting Up Basic Logging Middleware in Express.js

Creating a Simple Logging Middleware

Express.js allows you to create custom middleware for logging purposes. Let’s start with a basic example that logs details about every incoming request.

File: app.js

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

// Simple logging middleware
app.use((req, res, next) => {
  console.log(`${req.method} ${req.url} - ${new Date().toISOString()}`);
  next();
});

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

app.listen(3000, () => {
  console.log('Server running on http://localhost:3000');
});

				
			

Explanation:

  • Logging Middleware: This middleware logs the HTTP method, URL, and timestamp for each request.
  • next(): This function passes control to the next middleware in the stack, ensuring that the request is processed.

Output: When you visit http://localhost:3000/, the console will display:

				
					GET / - 2024-08-15T14:35:12.345Z

				
			

This log entry shows the request method (GET), the requested URL (/), and the timestamp of the request.

Adding Response Logging

You can also log information about the response, such as the status code and response time.

File: app.js (continued)

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

// Logging middleware for requests and responses
app.use((req, res, next) => {
  const start = Date.now();
  
  res.on('finish', () => {
    const duration = Date.now() - start;
    console.log(`${req.method} ${req.url} - ${res.statusCode} - ${duration}ms`);
  });

  next();
});

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

app.listen(3000, () => {
  console.log('Server running on http://localhost:3000');
});

				
			

Explanation:

  • Response Time Logging: This middleware logs the duration it took to process the request, in milliseconds (duration).
  • res.on('finish'): This event listener triggers when the response is finished, ensuring that the response status code is available for logging.

Output: After requesting http://localhost:3000/, the console might display:

				
					GET / - 200 - 15ms

				
			

This log entry includes the request method, URL, status code (200), and response time (15ms).

logging

Integrating Advanced Logging with morgan

What is morgan?

morgan is a popular logging middleware for Express.js that simplifies the process of logging HTTP requests. It provides predefined formats for logging, making it easy to set up consistent and informative logs.

Installing and Configuring morgan

You can install morgan using npm:

				
					npm install morgan

				
			

To use morgan in your application:

File: app.js (continued)

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

// Use 'morgan' for logging
app.use(morgan('combined'));

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

app.listen(3000, () => {
  console.log('Server running on http://localhost:3000');
});

				
			

Explanation:

  • morgan('combined'): This format logs comprehensive information including remote address, user agent, request method, URL, status code, and response time.

Output: After accessing http://localhost:3000/, the console might display something like:

				
					::1 - - [15/Aug/2024:14:40:12 +0000] "GET / HTTP/1.1" 200 13 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36"

				
			

This log entry contains detailed information about the request, including the client’s IP address, timestamp, request method, URL, protocol, status code, response size, referrer, and user agent.

Customizing morgan Formats

morgan allows you to customize the log format or create your own.

File: app.js (continued)

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

// Custom log format with 'morgan'
morgan.token('body', (req) => JSON.stringify(req.body));
app.use(morgan(':method :url :status :res[content-length] - :response-time ms :body'));

app.use(express.json());

app.post('/data', (req, res) => {
  res.send('Data received');
});

app.listen(3000, () => {
  console.log('Server running on http://localhost:3000');
});

				
			

Explanation:

  • Custom Token: The body token is added to log the request body in the logs.
  • Custom Format: The log format includes the request method, URL, status code, response size, response time, and request body.

Output: After making a POST request to http://localhost:3000/data with a JSON body, the console might display:

				
					POST /data 200 13 - 3.056 ms {"name":"John"}

				
			

This log entry includes the request method, URL, status code, response size, response time, and the JSON body sent with the request.

Implementing Custom Logging Middleware

Creating Custom Logging Middleware

While morgan is powerful, there might be scenarios where you need more control over the logging process. In such cases, you can create custom logging middleware.

File: app.js

				
					const express = require('express');
const fs = require('fs');
const path = require('path');
const app = express();

// Custom logging middleware
app.use((req, res, next) => {
  const log = `${req.method} ${req.url} - ${new Date().toISOString()}\n`;
  
  // Write logs to a file
  fs.appendFile(path.join(__dirname, 'access.log'), log, (err) => {
    if (err) {
      console.error('Failed to write to log file');
    }
  });

  console.log(log);
  next();
});

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

app.listen(3000, () => {
  console.log('Server running on http://localhost:3000');
});

				
			

Explanation:

  • Log to File: This middleware logs each request to both the console and a file named access.log.
  • fs.appendFile(): This function appends the log to the file, creating it if it doesn’t exist.

Output: Each request to http://localhost:3000/ will append a line to access.log and print it in the console:

				
					GET / - 2024-08-15T14:45:12.345Z

				
			

Logging Errors

Logging errors is crucial for debugging and monitoring the health of your application. You can create custom error logging middleware to capture and log errors in a structured format.

File: app.js (continued)

				
					// Error logging middleware
app.use((err, req, res, next) => {
  const errorLog = `${err.stack} - ${new Date().toISOString()}\n`;

  // Write error logs to a file
  fs.appendFile(path.join(__dirname, 'error.log'), errorLog, (err) => {
    if (err) {
      console.error('Failed to write to error log file');
    }
  });

  console.error(errorLog);
  res.status(500).send('Internal Server Error');
});

// Simulate an error for testing
app.get('/error', (req, res) => {
  throw new Error('This is a test error');
});

				
			

Explanation:

  • Error Logging: This middleware captures any errors, logs them to both the console and an error.log file, and sends a 500 status code to the client.
  • Simulated Error: Accessing /error will trigger an error, allowing you to test the error logging.

Output: After accessing http://localhost:3000/error, the error.log will contain:

				
					Error: This is a test error
    at /path/to/app.js:36:9
    ...
    - 2024-08-15T14:50:12.345Z

				
			

Best Practices

Log Only What You Need

Avoid logging sensitive information like passwords, API keys, or personal data. Always sanitize logs to prevent leaking sensitive information.

Structure Your Logs

Use a consistent structure for your logs to make them easier to parse and analyze. Consider using JSON format for logs if they need to be consumed by other systems.

Use a Logging Library

For more advanced logging, consider using libraries like winston or bunyan, which provide features like log levels, custom transports, and structured logging.

Monitor and Rotate Logs

Regularly monitor your logs for errors and performance issues. Implement log rotation to prevent log files from consuming too much disk space.

Advanced Logging with winston

What is winston?

winston is a versatile logging library for Node.js that supports various log transports (e.g., files, databases, console) and allows for structured, leveled logging.

Installing and Configuring winston

To use winston, install it via npm:

				
					npm install winston

				
			

File: app.js (continued)

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

// Set up 'winston' logger
const logger = winston.createLogger({
  level: 'info',
  format: winston.format.json(),
  transports: [
    new winston.transports.File({ filename: 'error.log', level: 'error' }),
    new winston.transports.File({ filename: 'combined.log' }),
  ],
});

if (process.env.NODE_ENV !== 'production') {
  logger.add(new winston.transports.Console({
    format: winston.format.simple(),
  }));
}

// Middleware to log all requests
app.use((req, res, next) => {
  logger.info(`${req.method} ${req.url}`);
  next();
});

// Example route
app.get('/', (req, res) => {
  res.send('Hello, World!');
});

// Simulate an error for testing
app.get('/error', (req, res) => {
  throw new Error('This is a test error');
});

// Error handling middleware
app.use((err, req, res, next) => {
  logger.error(err.stack);
  res.status(500).send('Internal Server Error');
});

app.listen(3000, () => {
  logger.info('Server running on http://localhost:3000');
});

				
			

Explanation:

  • Log Levels: winston allows you to set log levels (e.g., info, error). Logs with a severity below the specified level are ignored.
  • Log Transports: Logs can be sent to different outputs (error.log, combined.log, console).
  • Environment-Specific Logging: Logs are formatted differently in development vs. production.

Output:

  • combined.log: Logs all requests and info-level messages.
  • error.log: Logs error-level messages, such as the simulated error.

Logging is a fundamental aspect of building reliable and maintainable Express.js applications. By effectively implementing logging middleware, you can gain deep insights into your application's behavior, improve your ability to debug issues, and enhance overall performance. Whether using basic logging, the morgan middleware, or advanced libraries like winston, a well-structured logging system is essential for any Express.js project. Happy coding !❤️

Table of Contents

Contact here

Copyright © 2025 Diginode

Made with ❤️ in India