The definitive methodology for production application logging. Treat logs as event streams. Read →
console.log("user logged in") is debugging. Production logging is machine-readable, structured, and queryable. The goal: logs you can search, alert on, and correlate across services.
Log events, not messages. Instead of console.log("user 123 logged in"), emit {"event":"user.login","userId":123,"ts":"2025-..."}. Now you can query all logins for userId 123 across all servers.
npm install pino pino-pretty pino-http
import pino from 'pino';
const logger = pino({
level: process.env.LOG_LEVEL || 'info',
...(process.env.NODE_ENV !== 'production' && {
transport: { target: 'pino-pretty' } // Pretty in dev, JSON in prod
})
});
// Child logger with request context
app.use((req, res, next) => {
req.log = logger.child({ requestId: req.id, userId: req.user?.id });
next();
});
req.log.info({ event: 'order.placed', orderId: 123, amount: 29.99 });
req.log.warn({ event: 'rate_limit.hit', ip: req.ip });
req.log.error({ event: 'db.connection_failed', err: error.message });
# Docker logs to CloudWatch:
services:
api:
logging:
driver: awslogs
options:
awslogs-region: us-east-1
awslogs-group: /myapp/api
awslogs-create-group: "true"
# Query with CloudWatch Insights:
fields @timestamp, @message
| filter event = "order.placed"
| stats count() by bin(1h)
| sort @timestamp desc