Logging Guide
Overview
The application uses Python’s standardlogging module with a centralized configuration in app/utils/logger.py. It supports two output formats — plain text for development and structured JSON for production log aggregators (Datadog, ELK, CloudWatch).
Quick Start
get_logger(__name__) namespaces under app.* which makes filtering easier in aggregators.
Configuration
All logging configuration is driven by environment variables:| Variable | Default | Description |
|---|---|---|
APP_ENV | DEV | Controls default log level and format |
LOG_LEVEL | DEBUG (dev/test), INFO (prod) | Override log level |
LOG_FORMAT | text (dev/test), json (prod) | Output format: text or json |
SERVICE_NAME | syntropy-journals | Service identifier in structured logs |
Format Selection
| APP_ENV | Default LOG_FORMAT | Default LOG_LEVEL |
|---|---|---|
| DEV | text | DEBUG |
| TEST | text | DEBUG |
| PROD | json | INFO |
LOG_FORMAT=json make dev to test JSON output locally.
Output Formats
Text (development)
JSON (production)
Datadog Integration
The JSON format follows Datadog’s default attribute conventions:timestamp— parsed automatically as the log datelevel— mapped to Datadog severityservice— used for service filtering in Log Explorererror.kind,error.message,error.stack— populate the error panel
Setup with Datadog Agent
- Set environment variables in your deployment:
-
Configure the Datadog Agent to tail stdout (containerized) or the log file:
- For containerized deployments (Railway, Docker), Datadog auto-discovers JSON logs from stdout — no additional pipeline configuration needed.
Useful Datadog Queries
File Structure
Logger Naming Convention
| Pattern | Used By | Namespace |
|---|---|---|
get_logger(__name__) | Reflex states, UI functions, db_utils | app.<module.path> |
logging.getLogger(__name__) | LLM modules, API services, hydra_config | <module.path> |
get_logger(__name__) for the consistent app.* prefix which simplifies Datadog facet filtering.
Best Practices
- Use lazy formatting —
logger.info("User %s sent %d messages", user_id, count)not f-strings - Include
exc_info=Truefor errors —logger.error("Failed: %s", e, exc_info=True)to get stack traces in JSON output - Log at boundaries — Log when entering/exiting external calls (DB, LLM APIs), not inside tight loops
- Never log secrets — Don’t log API keys, tokens, or full connection strings
- Use structured context — For repeated context (session_id, user_id), include them in the message:
logger.info("session=%s action=send_message", session_id)