Logging¶
Craft Easy API uses structlog for structured logging with automatic request context binding.
Configuration¶
| Setting | Default | Description |
|---|---|---|
LOG_LEVEL |
"INFO" |
Minimum log level (DEBUG, INFO, WARNING, ERROR, CRITICAL) |
LOG_FORMAT |
"console" |
Output format: "console" (human-readable) or "json" (machine-parseable) |
LOG_SLOW_REQUEST_MS |
1000 |
Threshold in ms for slow request warnings |
Output Formats¶
Console Format (development)¶
Human-readable, colored output ideal for local development:
2026-04-06 12:34:56 [info ] Request completed method=GET path=/users status=200 duration_ms=45 request_id=abc-123
JSON Format (production)¶
Structured JSON for log aggregation systems (ELK, Datadog, Cloud Logging):
{
"timestamp": "2026-04-06T12:34:56.789Z",
"level": "info",
"event": "Request completed",
"method": "GET",
"path": "/users",
"status": 200,
"duration_ms": 45,
"request_id": "abc-123",
"tenant_id": "t-456",
"user_id": "u-789"
}
Request Context¶
Every log entry within a request automatically includes:
| Field | Source |
|---|---|
request_id |
From X-Request-ID header or auto-generated UUID |
tenant_id |
From authenticated user's tenant scope |
user_id |
From authenticated user's identity |
This context is bound via structlog's contextvars, so any log.info(...) call within the request lifecycle inherits it without explicit passing.
Slow Request Detection¶
Requests that exceed LOG_SLOW_REQUEST_MS (default: 1000ms) are logged with a warning:
2026-04-06 12:34:56 [warning ] Slow request method=GET path=/reports/revenue status=200 duration_ms=3200 request_id=abc-123
This helps identify performance bottlenecks without external monitoring.
Log Levels by Status¶
| Response Status | Log Level |
|---|---|
| 2xx | info |
| 4xx | info (client errors are normal) |
| 5xx | error |
| Slow (any status) | warning |
Silenced Loggers¶
Noisy third-party loggers are silenced to reduce log volume:
uvicorn.access— superseded by the structured logging middlewaremotor— MongoDB async driver internalspymongo— MongoDB driver internals
Usage¶
Get a logger for your module:
from craft_easy.core.logging import get_logger
log = get_logger(__name__)
async def process_payment(payment_id: str):
log.info("Processing payment", payment_id=payment_id)
# ...
log.info("Payment completed", payment_id=payment_id, amount=100.0)
The logger automatically includes any bound request context.