Difficulty: Intermediate
How do you implement logging in a production Node.js application? Compare Winston and Pino.
Production logging goes far beyond console.log. Structured logging libraries like Winston and Pino provide log levels, multiple output destinations (transports), JSON formatting, context metadata, and log rotation.
Log levels (error, warn, info, debug) let you control verbosity per environment. In production, log at info level and above. In development, include debug. Error logs should include stack traces, request context, and user IDs.
Winston is feature-rich with many transports (console, file, HTTP, cloud services). Pino is the fastest logger for Node.js (5-10x faster than Winston) because it uses asynchronous JSON serialization. For high-throughput APIs, Pino is the better choice.
Structured logging outputs JSON instead of text strings. This makes logs machine-parseable for log aggregation tools like ELK Stack, Datadog, or CloudWatch. Always include correlation IDs (request ID) to trace requests across services.
const winston = require('winston');
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json()
),
defaultMeta: { service: 'my-api' },
transports: [
// Write errors to error.log
new winston.transports.File({
filename: 'logs/error.log',
level: 'error',
maxsize: 5 * 1024 * 1024, // 5MB rotation
maxFiles: 5,
}),
// Write all logs to combined.log
new winston.transports.File({
filename: 'logs/combined.log',
}),
],
});
// Pretty print in development
if (process.env.NODE_ENV !== 'production') {
logger.add(new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(),
winston.format.simple()
),
}));
}
// Usage
logger.info('Server started', { port: 3000 });
logger.error('Database connection failed', { error: err.message });
Winston outputs structured JSON with timestamps and metadata. Transports send logs to different destinations. File rotation prevents disk overflow.
const pino = require('pino');
// Pino is 5-10x faster than Winston
const logger = pino({
level: process.env.LOG_LEVEL || 'info',
transport: process.env.NODE_ENV !== 'production'
? { target: 'pino-pretty', options: { colorize: true } }
: undefined,
redact: ['req.headers.authorization', 'password'], // Hide sensitive fields
});
// Express middleware for request logging
const pinoHttp = require('pino-http');
app.use(pinoHttp({ logger }));
// Usage
logger.info({ userId: 123 }, 'User logged in');
logger.error({ err, requestId: req.id }, 'Payment failed');
logger.warn({ remaining: 5 }, 'Rate limit approaching');
// Child logger with persistent context
const userLogger = logger.child({ userId: req.user.id });
userLogger.info('Profile updated'); // Includes userId automatically
userLogger.info('Password changed');
Pino uses numeric log levels for speed and JSON output. Child loggers inherit context. redact hides sensitive fields automatically.
const { v4: uuidv4 } = require('uuid');
// Add request ID for tracing
app.use((req, res, next) => {
req.id = req.headers['x-request-id'] || uuidv4();
res.setHeader('X-Request-ID', req.id);
next();
});
// Log request/response
app.use((req, res, next) => {
const start = Date.now();
// Log when response finishes
res.on('finish', () => {
const duration = Date.now() - start;
const logData = {
requestId: req.id,
method: req.method,
url: req.originalUrl,
status: res.statusCode,
duration: `${duration}ms`,
userAgent: req.headers['user-agent'],
ip: req.ip,
};
if (res.statusCode >= 400) {
logger.warn(logData, 'Request failed');
} else {
logger.info(logData, 'Request completed');
}
});
next();
});
// In error handler
app.use((err, req, res, next) => {
logger.error({
requestId: req.id,
err: { message: err.message, stack: err.stack },
path: req.path,
}, 'Unhandled error');
res.status(500).json({ error: 'Internal server error' });
});
Request IDs enable tracing a request across logs and services. Log duration, status, and context for every request. Warn on 4xx, error on 5xx.
Winston, Pino, Log Levels, Structured Logging, Transports