When building a Node.js/Express app, logging is essential for debugging, monitoring, and keeping track of application behavior — both during development and in production.
In this post, we'll explore two powerful logging tools for Express:
- Morgan – A lightweight HTTP request logger
- Winston – A robust, customizable logger for application-level events
Why Logging Matters
Logging helps you:
- Track user requests and responses
- Debug server-side errors
- Monitor performance and behavior in production
- Audit events or suspicious activity
Morgan: HTTP Request Logger
📌 What is Morgan?
Morgan logs incoming HTTP requests in a predefined format. It's ideal for development and debugging routes.
Step 1: Install Morgan
npm install morgan
Step 2: Use Morgan in Your App
import express from 'express';
import morgan from 'morgan';
const app = express();
// Use 'dev' preset for concise logging
app.use(morgan('dev'));
app.get('/', (req, res) => {
res.send('Hello World');
});
app.listen(3000, () => console.log('Server running'));
Output Example:
GET / 200 8.104 ms - 11
You can also use other formats like 'combined'
, 'short'
, or define your own:
app.use(morgan(':method :url :status :response-time ms'));
Winston: Flexible Application Logger
📌 What is Winston?
Winston is a general-purpose logging library. It allows you to:
- Log messages to console, files, or external services
- Set log levels (info, warn, error, etc.)
- Format logs (JSON, timestamps, custom formats)
Step 1: Install Winston
npm install winston
Step 2: Create a Logger Configuration
Create a logger.js
file:
import winston from 'winston';
const logger = winston.createLogger({
level: 'info', // minimum level to log
format: winston.format.combine(
winston.format.timestamp(),
winston.format.printf(
({ timestamp, level, message }) =>
`[${timestamp}] ${level.toUpperCase()}: ${message}`
)
),
transports: [
new winston.transports.Console(), // log to console
new winston.transports.File({ filename: 'logs/error.log', level: 'error' }),
new winston.transports.File({ filename: 'logs/combined.log' })
]
});
export default logger;
Step 3: Use Winston in Your Routes
import logger from './logger.js';
app.get('/health', (req, res) => {
logger.info('Health check route hit');
res.send('OK');
});
app.get('/error', (req, res) => {
logger.error('Something went wrong');
res.status(500).send('Error');
});
Combining Morgan with Winston
You can use Morgan to log HTTP requests and pipe those logs into Winston for consistent formatting and file storage.
import morgan from 'morgan';
import logger from './logger.js';
const stream = {
write: (message) => logger.info(message.trim())
};
app.use(morgan('combined', { stream }));
Summary
Tool | Purpose | Best Use Case |
---|---|---|
Morgan | HTTP request logger | Log incoming HTTP requests |
Winston | Application-level logger | Track custom events/errors |
Final Thoughts
- Use Morgan during development to trace HTTP traffic.
- Use Winston in production for advanced logging, error tracking, and audit logs.
- Combine both for full-stack visibility.
Top comments (0)