← Course Index

Logging in Production

~25 min · Monitoring

Ref
Primary Source
12 Factor App — Logs

The definitive methodology for production application logging. Treat logs as event streams. Read →

Structured Logging

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.

Pino — Fast Structured Logging for Node.js

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 });

What to Log / Not Log

CloudWatch — Centralized Log Aggregation on AWS

# 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

Check Your Understanding

1. Which log level for a database connection failure?
2. Which data should you NEVER include in a log entry?
3. What makes it possible to search all orders placed in the last hour across all your app servers?