Logging best practices
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Effective logging gives you visibility into application behavior without requiring a debugger or manual reproduction. Poor logging either floods storage with noise or provides no useful information when incidents occur. The goal is structured, leveled, contextual log output that helps you diagnose problems quickly in production.
Use Log Levels Consistently
Log levels categorize message importance. Define team conventions for what each level means and enforce them in code review.
| Level | When to use | Example |
| DEBUG | Internal state useful during development | "Cache miss for key=user:42" |
| INFO | Normal operational events | "Server started on port 8080" |
| WARNING | Unexpected but recoverable situations | "Retry attempt 2/3 for payment API" |
| ERROR | Failed operations that need investigation | "Database connection refused after 3 retries" |
| CRITICAL | System-wide failures requiring immediate action | "Out of disk space on /data volume" |
Use Structured Logging
Plain text logs are hard to search and aggregate. Structured logging (JSON format) makes logs machine-parseable for tools like ELK, Datadog, and CloudWatch.
In Go, use slog (standard library since Go 1.21):
Include Context in Every Log Line
A log message without context is useless during incident response. Always include identifiers that let you trace the event.
Essential context fields:
- Request ID or trace ID — correlate logs across services
- User or tenant ID — identify who is affected
- Operation name — what the code was trying to do
- Duration — how long the operation took
Do Not Log Sensitive Data
Never log passwords, API keys, tokens, credit card numbers, or personal health information. Sanitize or redact before logging.
Set Appropriate Log Retention and Rotation
Logs consume disk space. Configure rotation to prevent disk exhaustion.
For Kubernetes, log to stdout and let the container runtime handle rotation. Use a centralized log aggregator (ELK, Loki, Datadog) for search and retention policies.
Avoid Common Anti-Patterns
Common Pitfalls
- Logging at DEBUG level in production and flooding storage with gigabytes of noise per hour.
- Using
print()instead of a logging framework, losing level filtering, rotation, and structured output. - Logging inside tight loops without rate limiting, which can degrade application performance.
- Including sensitive data (passwords, tokens, PII) in log messages that end up in shared log aggregators.
- Not including correlation IDs, making it impossible to trace a request across multiple services.
Summary
- Use consistent log levels with team-agreed definitions for each level.
- Use structured (JSON) logging for machine-parseable output.
- Include request IDs, user IDs, and operation context in every log line.
- Never log sensitive data — redact tokens, passwords, and PII.
- Configure log rotation and retention to prevent disk exhaustion.
- Use lazy formatting (
logger.info("msg %s", val)) to avoid unnecessary string construction.

