Logging
Structured logging (structlog)
Waldur uses structlog via django-structlog for structured logging. All logs are emitted as JSON in production (or readable console output in development when WALDUR_DEV_LOGS=1).
Existing stdlib logging calls work without changes: logging.getLogger(__name__) and logger.info("Order %s created", order.uuid) are processed through structlog's foreign_pre_chain and produce structured output with timestamp, level, logger name, request_id, user_uuid (in HTTP context), etc.
Example JSON output:
1 | |
Configuration
- Console: JSON (default) or colored console output when
WALDUR_DEV_LOGS=1 - Database: SystemLog table receives JSON messages via
DatabaseLogHandler - Celery: Workers use structlog with task context (request_id, task_id)
Example Celery task log:
1 | |
Adding structured fields
For explicit structured fields (e.g. for log aggregation queries), use extra:
1 | |
The event_logger (see below) automatically includes event_type and event_context in logs.
Customizing logging
Override LOGGING in your settings.py to add file output, syslog, or external aggregators. Extend the base config (e.g. via copy.deepcopy) rather than replacing it entirely. The snippets below can be combined.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | |
Event-only forwarding to a log server (e.g. for audit pipelines): use filters RequireEvent / RequireNotEvent from waldur_core.logging.log to separate events from general logs:
1 2 3 4 5 6 7 8 9 10 11 | |
Per-logger level overrides:
1 2 | |
Event logging
Event log entries is something an end user will see. In order to improve user experience the messages should be written in a consistent way.
Here are the guidelines for writing good log events.
- Use present perfect passive for the message.
Right: Environment %s has been created.
Wrong: Environment %s was created.
- Build a proper sentence: start with a capital letter, end with a period.
Right: Environment %s has been created.
Wrong: environment %s has been created
- Include entity names into the message string.
Right: User %s has gained role of %s in project %s.
Wrong: User has gained role in project.
- Don't include too many details into the message string.
Right: Environment %s has been updated.
Wrong: Environment has been updated with name: %s, description: %s.
- Use the name of an entity instead of its
__str__.
Right: event_logger.info('Environment %s has been updated.', env.name)
Wrong: event_logger.info('Environment %s has been updated.', env)
- Don't put quotes around names or entity types.
Right: Environment %s has been created.
Wrong: Environment "%s" has been created.
- Don't capitalize entity types.
Right: User %s has gained role of %s in project %s.
Wrong: User %s has gained Role of %s in Project %s.
- For actions that require background processing log both start of the process and its outcome.
Success flow:
-
log
Environment %s creation has been started.within HTTP request handler; -
log
Environment %s has been created.at the end of background task.
Failure flow:
-
log
Environment %s creation has been started.within HTTP request handler; -
log
Environment %s creation has failed.at the end of background task. -
For actions that can be processed within HTTP request handler log only success.
Success flow:
log User %s has been created. at the end of HTTP request handler.
Failure flow:
don't log anything, since most of the errors that could happen here are validation errors that would be corrected by user and then resubmitted.