Skip to content

πŸ“‹ Logging Practices ​

Structured, useful logging that helps debug production issues.

Log levels ​

LevelWhen to useExample
errorSomething broke, needs attentionUnhandled exception, failed payment
warnSomething unexpected but handledRetry succeeded, deprecated API used
infoKey business eventsUser signed up, order placed
debugDevelopment detailsQuery executed, cache hit/miss

Structured logging ​

typescript
// BAD
console.log('User ' + userId + ' placed order ' + orderId)

// GOOD
logger.info('Order placed', {
  userId,
  orderId,
  amount: order.total,
  requestId: req.id,
})

Output (JSON):

json
{
  "level": "info",
  "message": "Order placed",
  "userId": "usr_123",
  "orderId": "ord_456",
  "amount": 99.99,
  "requestId": "req_789",
  "timestamp": "2026-03-06T12:00:00Z"
}

What to log ​

  • Incoming requests (method, path, status, duration)
  • Business events (signup, purchase, cancellation)
  • External API calls (URL, status, duration)
  • Errors with full context
  • Background job execution (start, end, result)

What NOT to log ​

  • Passwords, tokens, API keys
  • Full credit card numbers
  • Personal data (email, phone) unless necessary
  • Request/response bodies in production (too verbose)
  • Health check pings (noise)

Request context ​

Add a unique request ID to every log in a request lifecycle:

typescript
app.use((req, res, next) => {
  req.id = crypto.randomUUID()
  next()
})

This enables tracing a single request across all log entries.

Log aggregation ​

  • Development: console output
  • Production: Ship to ELK, Loki, Datadog, or CloudWatch
  • Set retention policies (30 days for debug, 1 year for errors)
  • Create alerts on error patterns

Pergame Knowledge Base