Your production app shouldn't have console.log("here") or console.log(user) scattered everywhere. Here's how to build logging that actually helps when things break.
Use a Logging Library
// Bad
console.log("User created:", user);
console.log("Error:", err);
// Good (using Winston)
const winston = require('winston');
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.Console(),
new winston.transports.File({ filename: 'app.log' })
]
});
logger.info('User created', { userId: user.id, email: user.email });
logger.error('Failed to create user', { error: err.message, stack: err.stack });
Log Levels
error → Something broke. Needs immediate attention.
warn → Something unexpected but handled. Worth investigating.
info → Normal operations. "User logged in", "Order processed".
debug → Development details. Request payloads, intermediate values.
In production: LOG_LEVEL=info. In development: LOG_LEVEL=debug.
Structured Logging
// Bad: unstructured text
logger.info(`User ${userId} placed order ${orderId} for $${total}`);
// Good: structured JSON
logger.info('Order placed', {
userId,
orderId,
total,
items: order.items.length
});
Structured logs are searchable. You can query:
- "Show all logs where
userId=123" - "Show all orders where
total > 1000"
Text logs require regex parsing.
Request Logging Middleware
const { v4: uuid } = require('uuid');
app.use((req, res, next) => {
req.requestId = uuid();
const start = Date.now();
res.on('finish', () => {
logger.info('Request completed', {
requestId: req.requestId,
method: req.method,
path: req.path,
statusCode: res.statusCode,
duration: Date.now() - start,
userAgent: req.get('user-agent'),
ip: req.ip
});
});
next();
});
What to Log
// DO log:
logger.info('Payment processed', { orderId, amount, provider: 'stripe' });
logger.warn('Rate limit approaching', { userId, current: 95, limit: 100 });
logger.error('Database connection failed', { host, retryIn: '5s' });
// DON'T log:
logger.info('Entering function processOrder'); // Noise
logger.debug(req.body); // May contain passwords
logger.info(`User password: ${user.password}`); // Never log secrets
Log Rotation
// Winston with rotation
const DailyRotateFile = require('winston-daily-rotate-file');
const transport = new DailyRotateFile({
filename: 'logs/app-%DATE%.log',
datePattern: 'YYYY-MM-DD',
maxSize: '20m',
maxFiles: '14d' // Keep 14 days
});
Without rotation, log files grow until they fill your disk.
The Golden Rule
Log as if the person reading it is debugging a production incident at 3 AM, has never seen the code, and the only context they have is what's in the log.
What logging setup do you use? Any tools you'd recommend?
Top comments (0)