Why I Use Loggers like Winston and Morgan in My Node.js Apps Instead of Console Logs

Why I Use Loggers like Winston and Morgan in My Node.js Apps Instead of Console Logs

Unlocking the Power of Professional Logging for Scalable Applications

As a Node.js developer, debugging and monitoring are integral parts of my workflow. While console.log() might seem like the simplest and most obvious tool for this purpose, it’s far from the best option when building scalable and maintainable applications. This is where professional logging libraries like Winston and Morgan come into play.

In this blog, I’ll dive into why I rely on Winston and Morgan instead of sticking to console logs, showcasing their features with examples and comparisons.


Why Console Logs Are Not Enough

  1. Lack of Log Levels : With console.log(), you don’t have a structured way to classify logs like info, error, warn, or debug.

    example :

     console.log("This is an info log");
     console.error("This is an error log");
     // But no way to categorize further or manage verbosity.
    
  2. Unstructured Outputs: Console logs do not offer formatting or timestamps by default, making it hard to track when or where a log was generated.

  3. No Persistence: Logs created via console.log() are ephemeral. They’re printed in the terminal and lost once the application stops running.

  4. Performance Issues: Excessive console logs can slow down your application, especially when logging in production.

  5. Inadequate for Production: Console logs aren’t suitable for real-world monitoring and debugging, especially in distributed systems where logs need to be centralized.


Enter Winston and Morgan

Both Winston and Morgan are powerful logging tools, but they serve different purposes:

  • Winston: A general-purpose logger with advanced features like log levels, transports (e.g., saving to files or sending to monitoring systems), and formatting.

  • Morgan: A specialized HTTP request logger middleware for Node.js, perfect for tracking incoming requests and responses in your web application.

Key Benefits of Winston

  1. Log Levels: Categorize logs into levels like error, warn, info, debug, etc., and filter them based on the environment.

     import winston from "winston";
     import config from "./config.js";
    
     export const logger = winston.createLogger({
       level: "info",
       format: winston.format.combine(
         winston.format.timestamp({
           format: "ddd, MMM D YYYY : h:mm A",
         }),
         winston.format.colorize(),
         winston.format.printf((info) => {
           return `${info.level}: ${info.message}  ->  ${info.timestamp}`;
         })
       ),
       // transports: [
       //   new winston.transports.File({ filename: "error.log", level: "error" }),
       //   new winston.transports.File({ filename: "combined.log" }),
       // ],
     });
    
     logger.info("Application started");
     logger.error("Something went wrong");
    
  2. Transports: Send logs to multiple destinations, such as files, databases, or external monitoring services.

  3. Format Customization: Add timestamps, colors, or custom formats for better readability.

  4. Error Handling: Capture stack traces and structured error information.

Key Benefits of Morgan

  1. Request Logging: Morgan logs HTTP requests, including method, URL, response time, and status code.

     const morgan = require("morgan");
     const express = require("express");
     const {logger} = require("./logger.js");
    
     const app = express();
    
     app.use(morgan("combined")); // Logs detailed HTTP request info
    
     app.get("/", (req, res) => res.send("Hello, world!"));
    
     app.listen(3000, () => logger.info("Application started"));
    
  2. Predefined Formats: Use presets like combined, common, short, and dev or define your own custom format.

  3. Lightweight and Fast: Optimized for HTTP request logging without overhead.


Comparing Winston, Morgan, and Console Logs

Example 1: Basic Logging

With Console Logs:

console.log("User logged in");
console.error("Database connection failed");

With Winston :

logger.info("User logged in");
logger.error("Database connection failed", { code: 500 });
  • Winston adds timestamps and allows categorization.

Example 2: HTTP Request Logging

With Console Logs :

app.use((req, res, next) => {
  console.log(`${req.method} ${req.url}`);
  next();
});

With Morgan :

app.use(morgan("combined"));
  • Morgan provides detailed logs without manual effort.

Visual Comparison

Console Logs vs. Winston Output

Console Logs:

User logged in
Database connection failed

Winston Logs:

info: Server running in DEVELOPMENT mode on port 5000  ->  Tue, Jan 28 2025 : 12:36 PM

Morgan Output (Combined Format)

127.0.0.1 - - [28/Jan/2025:12:34:56 +0000] "GET / HTTP/1.1" 200 - "-" "Mozilla/5.0"

When to Use Which

  • Use Winston for general application logging, error tracking, and debugging.

  • Use Morgan for HTTP request and response logging in Express applications.

  • Avoid relying on console.log() in production applications.


Final Thoughts

Using logging libraries like Winston and Morgan is a no-brainer for anyone serious about building robust and maintainable Node.js applications. They provide powerful features, better organization, and ensure that your logs are useful for debugging and monitoring both during development and in production.

If you’re still using console.log() in your apps, it’s time to make the switch. Your future self (and your team) will thank you!


Additional Resources