logger.debug("Received request for user_id=%s with payload=%s", user_id, payload)
Best practices for effective debug logging
Be selective: Log what matters
Avoid logging every single operation; focus on:
- Function entry/exit points
- Conditional branches
- Variable values that alter execution flow
- Exception paths and key external calls.
Excessive debug logs become noise and impact performance.
Structure and context: Make logs actionable
- Structured logging: Use formats like JSON. This enables automation, easier parsing and search capabilities.
json
{
"timestamp": "2025-09-09T07:00:00Z",
"level": "DEBUG",
"component": "auth",
"message": "User authentication failed",
"user_id": "abc123",
"reason": "Password expired"
}
- Be descriptive: Every message should clearly explain what happened, where and why.
- Include context: Add request or correlation IDs, user IDs, error codes, trace IDs or relevant method names
- Instead of logger.debug(“API request failed”), use: logger.debug(“API request failed: req_id=%s, user=%s, status=%d”, req_id, user_id, resp.status_code)
Consistent formatting and levels
- Choose and enforce a log line structure across services.
- Use log levels properly. Reserve DEBUG for development/troubleshooting, ERROR for actionable failures and so on.
- Avoid using DEBUG in production unless needed and filtered; it can leak too much information and slow systems.
Advanced technical techniques
Correlation IDs for distributed tracing
- Assign a unique identifier to each request that propagates through all microservices
- Log this ID at every service boundary to reconstruct the exact request flow during analysis.
python
logger.debug("Processing payment", extra={"correlation_id": cid, "user_id": uid})
Parameterized logging
- Prefer parameterized log statements to prevent costly string construction when DEBUG logging is disabled.
java
logger.debug("Order processed for user {}: amount {}", userId, amount);
Automated sampling and rate limiting
- For high-traffic systems, implement log sampling to avoid log storms.
- Rate-limited logging ensures only a set number of verbose logs are stored per period, throttling excessive output.
Defensive logging
- Prevent logs themselves from triggering failures by wrapping complex serializations in try-except blocks.
python
try:
logger.debug("Complex object state: %s", complex_object.to_json())
except Exception:
pass
Centralized log management
- Use platforms (ELK stack, Graylog, Middleware, etc.) for:
- Aggregating logs from many sources.
- Building powerful search, dashboarding and alerting workflows.
Common pitfalls to avoid
- Over-logging: Produces too much noise, slows down systems and hides real issues.
- Logging sensitive data: Never log passwords, tokens or user PII.
- Unclear messages: Avoid vague lines like “Something broke.” Specify action, object and context.
- Ignoring performance: Debug logs in the hot path of performance-sensitive applications without throttling or conditional inclusion can add serious latency.
- Inconsistent format: Hinders log aggregation and automated alerts.
- Node.js: Winston, Bunyan for structured, multi-transport logging
- Python: Logging module (with JSON formatter), structlog
- Java: SLF4J/Logback
- .NET: Serilog
- Aggregation: ELK Stack, Graylog, Datadog, Middleware
Sample code snippets
Node.js with Winston
javascript