Logging Best Practices
This document describes the logging standards and best practices for the Substrate project.
Overview
Substrate uses structured logging to provide consistent, searchable, and contextual log output across all services. Logs include:
- Timestamps - ISO 8601 format with milliseconds
- Severity levels - TRACE, DEBUG, INFO, WARN, ERROR, FATAL
- Context - Component names, request paths, user IDs, and other metadata
- Error details - Error messages, stack traces, and relevant context
Server-Side Logging
For server-side code (API routes, GraphQL resolvers, middleware, etc.), use the logger from shared-utils:
- Use
createLogger()for general components - Use
createRouteLogger()for API routes (automatically extracts path from request) - Use
createResolverLogger()for GraphQL resolvers
Client-Side Logging
For client-side React components, use createClientLogger() from shared-utils. Client logs are formatted for browser console output with timestamps and severity levels.
Log Levels
Choose the appropriate log level:
- TRACE - Very detailed diagnostic information (development only)
- DEBUG - Detailed information useful for debugging (development only)
- INFO - General informational messages about application flow
- WARN - Warning messages for potentially harmful situations
- ERROR - Error messages for failures that don't stop the application
- FATAL - Critical errors that may cause the application to terminate
Context Best Practices
Always include relevant context in your logs:
For API Routes
Create logger inside handler to capture the route path automatically. Include method, userId, and operation details as context.
For GraphQL Resolvers
Create logger without resolver name and add operation details in the context when logging.
For Background Jobs
Use createLogger() with a component name and include job-specific context like queueSize and batchId.
What to Log
DO Log:
- ✅ Request/response lifecycle events
- ✅ Business logic decisions
- ✅ External service calls and responses
- ✅ Performance metrics
- ✅ Security events (auth failures, access denials)
- ✅ Errors with full context and stack traces
- ✅ State changes in critical operations
DON'T Log:
- ❌ Sensitive data (passwords, tokens, API keys, PII)
- ❌ Full request/response payloads in production
- ❌ Noisy information in tight loops
- ❌ Stack traces for expected errors (e.g., validation failures)
Environment-Specific Behavior
The logging system automatically adjusts based on environment:
Development
- Pretty-printed, colorized output
- All log levels enabled (including DEBUG and TRACE)
- Full stack traces included
Production
- JSON structured output
- INFO level and above
- Optimized for log aggregation systems
Configuration
Configure logging via environment variables:
# Set log level (default: 'info' in production, 'debug' in development)
LOG_LEVEL=debug
# Node environment
NODE_ENV=production
Docker Container Logs
When running in Docker containers, logs are automatically output to stdout in JSON format with ISO 8601 timestamps, uppercase severity levels, component context, and structured fields for easy parsing. The logger uses synchronous writes to ensure logs are immediately flushed to stdout, which is essential for containerized and serverless environments where async logging may not flush before the process exits.
To view debug-level logs in Docker, set the LOG_LEVEL environment variable:
docker run -e LOG_LEVEL=debug substrate-web
These logs can be easily consumed by log aggregation systems like CloudWatch Logs, Datadog, Splunk, ELK Stack, or Grafana Loki.
Troubleshooting
Missing Timestamps
If logs don't show timestamps, ensure you're using the logger from shared-utils instead of console.log.
Logs Not Appearing
Check the LOG_LEVEL environment variable. In production, only INFO and above are logged by default.
Too Verbose Logs
Set LOG_LEVEL=warn or LOG_LEVEL=error to reduce noise.
Migration from console.*
Replace all console.log, console.error, etc. with the appropriate logger. Instead of using console methods, use the logger's methods (info, error, warn, etc.) with proper context objects.
Further Reading
- Pino Documentation - The underlying logging library (npm)
- Structured Logging Best Practices
- 12-Factor App Logs