structlog - Quick Reference
When to Use This Skill
- Structured logging in Python
- Integration with JSON logging
- Context binding for request tracing
Deep Knowledge: Use mcp__documentation__fetch_docs with technology: structlog for comprehensive documentation.
Basic Setup
pip install structlog
Essential Patterns
Basic Configuration
import structlog
structlog.configure(
processors=[
structlog.stdlib.filter_by_level,
structlog.stdlib.add_logger_name,
structlog.stdlib.add_log_level,
structlog.stdlib.PositionalArgumentsFormatter(),
structlog.processors.TimeStamper(fmt="iso"),
structlog.processors.StackInfoRenderer(),
structlog.processors.format_exc_info,
structlog.processors.UnicodeDecoder(),
structlog.processors.JSONRenderer()
],
wrapper_class=structlog.stdlib.BoundLogger,
context_class=dict,
logger_factory=structlog.stdlib.LoggerFactory(),
cache_logger_on_first_use=True,
)
Basic Usage
import structlog
log = structlog.get_logger()
log.info("user_logged_in", user_id=123, ip="192.168.1.1")
log.warning("rate_limit_exceeded", endpoint="/api/users", count=100)
log.error("database_error", error="connection timeout", retry=3)
Context Binding
log = structlog.get_logger()
# Bind context for all subsequent logs
log = log.bind(request_id="abc-123", user_id=42)
log.info("processing_started") # Includes request_id and user_id
log.info("step_completed", step=1)
log.info("processing_finished")
# New context
log = log.new(request_id="xyz-789")
FastAPI Integration
from fastapi import FastAPI, Request
import structlog
app = FastAPI()
@app.middleware("http")
async def add_request_context(request: Request, call_next):
structlog.contextvars.clear_contextvars()
structlog.contextvars.bind_contextvars(
request_id=request.headers.get("X-Request-ID", str(uuid.uuid4())),
path=request.url.path,
)
return await call_next(request)
Django Integration
# settings.py
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"json": {
"()": structlog.stdlib.ProcessorFormatter,
"processor": structlog.processors.JSONRenderer(),
},
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"formatter": "json",
},
},
"root": {
"handlers": ["console"],
"level": "INFO",
},
}
Exception Logging
try:
risky_operation()
except Exception:
log.exception("operation_failed", operation="risky")
# Automatically includes stack trace
When NOT to Use This Skill
- Simple scripts: Standard logging module is sufficient for basic needs
- Legacy codebases: Migration effort may not be worth it for small projects
- Text-only log requirements: structlog is JSON-first, requires parsing
- Non-Python projects: Use language-appropriate logging frameworks
- Applications without centralized logging: Standard logging may be simpler
Anti-Patterns
| Anti-Pattern |
Why It's Bad |
Solution |
| Using ConsoleRenderer in production |
Wastes CPU, not machine-parseable |
Use JSONRenderer for production |
| Not clearing context variables |
Leaks context across requests |
Use structlog.contextvars.clear_contextvars() |
| Logging large objects |
Serialization overhead |
Log only necessary fields or IDs |
| Creating new logger per request |
Performance overhead |
Use logger.bind() to add context |
| Missing exception logging |
Loses stack traces |
Use logger.exception() in except blocks |
| Not configuring processors |
Incomplete/inconsistent output |
Configure full processor pipeline |
Quick Troubleshooting
| Issue |
Cause |
Solution |
| Plain text output instead of JSON |
ConsoleRenderer configured |
Change to JSONRenderer() in processors |
| Context not appearing in logs |
Not using context binding |
Use logger.bind() or contextvars |
| Performance issues |
Too many processors |
Remove unnecessary processors, use JSONRenderer |
| Missing timestamps |
No TimeStamper processor |
Add TimeStamper(fmt='iso') to processors |
| Logs not colorized in dev |
Missing dev configuration |
Use ConsoleRenderer(colors=True) for development |
| Context bleeding across requests |
Not clearing contextvars |
Clear context at request start with middleware |