How Logging Works in Plane: JSON Formatting, Custom Handlers, and Security
Plane uses Python's standard logging framework with structured JSON output, custom time-and-size based log rotation, and segregated loggers for requests, workers, and exceptions, while hashing sensitive API tokens for security.
The open-source project management tool Plane (makeplane/plane) implements a comprehensive logging strategy built on Python's native logging module. The system is designed for production observability, featuring structured JSON formatting, automatic log rotation, and secure handling of sensitive authentication data. Understanding how logging in Plane works helps operators monitor API health, debug background tasks, and maintain audit trails without exposing secrets.
Logging Architecture and Configuration
The production logging configuration in Plane is defined as a comprehensive LOGGING dictionary in [apps/api/plane/settings/production.py](https://github.com/makeplane/plane/blob/preview/apps/api/plane/settings/production.py). This configuration establishes multiple specialized loggers that isolate different operational concerns.
JSON Formatting for Structured Logging
Console output uses the JSON formatter provided by pythonjsonlogger, enabling seamless integration with log aggregation systems. The configuration specifies "json" as the formatter class for the console handler, ensuring all log records include standardized fields like timestamp, level, and message in machine-readable format.
Custom Rotating File Handler
For file persistence, Plane implements a custom SizedTimedRotatingFileHandler in [apps/api/plane/utils/logging.py](https://github.com/makeplane/plane/blob/preview/apps/api/plane/utils/logging.py). This handler extends logging.handlers.TimedRotatingFileHandler and adds a maxBytes parameter, triggering rollover when either the time interval (configured as "when": "s") expires or the file size threshold is exceeded. This dual-criteria approach prevents uncontrolled disk usage while ensuring logs rotate frequently enough for manageable file sizes.
Segregated Logger Hierarchy
The configuration defines distinct logger namespaces to separate concerns:
plane.api.request– HTTP request/response detailsplane.api– General API operationsplane.worker– Background Celery task logsplane.exception– Exception tracing and debuggingplane.authentication,plane.external,plane.migrations– Feature-specific operational logs
Request Logging and API Security
Plane captures detailed HTTP traffic and API usage through Django middleware while implementing security measures to protect sensitive credentials.
HTTP Request Logger Middleware
The RequestLoggerMiddleware in [apps/api/plane/middleware/logger.py](https://github.com/makeplane/plane/blob/preview/apps/api/plane/middleware/logger.py) automatically records every incoming HTTP request (excluding health checks). It logs the HTTP method, path, status code, duration, client IP, user agent, and authenticated user ID via the plane.api.request logger. This provides complete visibility into API usage patterns and performance characteristics.
Secure API Token Logging
The APITokenLogMiddleware (located in the same file) handles external API calls that include an X-Api-Key header. Rather than logging the raw token, the middleware:
- Hashes the key using HMAC-SHA256 with the Django
SECRET_KEYto create a non-reversible identifier - Redacts sensitive headers from the log output
- Queues the structured log payload via the
process_logsCelery task for asynchronous persistence
This approach allows API usage tracking and debugging while cryptographically protecting the actual token values.
Exception and Background Task Logging
Beyond HTTP traffic, Plane provides specialized utilities for error tracking and asynchronous job monitoring.
Centralized Exception Logger
The log_exception helper function in [apps/api/plane/utils/exception_logger.py](https://github.com/makeplane/plane/blob/preview/apps/api/plane/utils/exception_logger.py) provides a standardized way to record exceptions to the plane.exception logger. When DEBUG mode is enabled, the function automatically includes full stack traces; in production, it can optionally log as warnings to prevent error-level noise for known recoverable issues.
Celery Worker Logging
Background tasks emit logs through the plane.worker logger. The logger_task in [apps/api/plane/bgtasks/logger_task.py](https://github.com/makeplane/plane/blob/preview/apps/api/plane/bgtasks/logger_task.py) receives structured payloads from the token middleware and persists them to the database asynchronously. This separation ensures that logging operations do not block the request/response cycle.
Practical Usage Examples
When extending Plane or debugging custom modules, use these patterns to integrate with the existing logging infrastructure:
# Creating a logger for a custom module
import logging
logger = logging.getLogger("plane.custom_module")
logger.info("Custom event happened", extra={"detail": "value"})
# Using the request logger (automatically invoked by middleware)
# In a view you can add extra context if needed
logger = logging.getLogger("plane.api.request")
logger.debug("Extra debug info", extra={"user_id": request.user.id})
# Manually logging an exception
from plane.utils.exception_logger import log_exception
try:
risky_operation()
except Exception as exc:
log_exception(exc) # logs with stack trace when DEBUG=True
Summary
- Structured JSON logging: Plane uses pythonjsonlogger for console output, enabling integration with log aggregation platforms while maintaining human readability in development.
- Dual-criteria rotation: The custom
SizedTimedRotatingFileHandlercombines time-based and size-based rotation to manage disk usage efficiently in production environments. - Security-first token handling: API keys are hashed with HMAC-SHA256 before logging, ensuring sensitive credentials remain protected while maintaining auditability through the
plane.api.requestchannel. - Component isolation: Separate loggers for requests (
plane.api.request), workers (plane.worker), and exceptions (plane.exception) allow fine-grained log level configuration and filtering. - Asynchronous persistence: API token logs are processed via Celery tasks (
logger_task) to prevent logging I/O from impacting request latency.
Frequently Asked Questions
How does Plane prevent sensitive API keys from appearing in logs?
Plane's APITokenLogMiddleware hashes the X-Api-Key header using HMAC-SHA256 with the Django SECRET_KEY before logging, creating a non-reversible identifier. The middleware also redacts sensitive headers and delegates persistence to a background Celery task, ensuring raw tokens never touch the log files or database directly.
What triggers log file rotation in Plane's production environment?
The custom SizedTimedRotatingFileHandler rotates files based on two criteria: time intervals (configured with "when": "s" for seconds) and maximum file size (maxBytes). A new log file is created when either the time threshold expires or the size limit is reached, whichever occurs first.
Where should I send logs from custom background tasks in Plane?
Use the plane.worker logger namespace for all Celery task logging. This segregates background job output from HTTP request logs and allows operators to configure different retention policies or alerting rules for asynchronous operations.
How can I enable stack traces in Plane's exception logs?
Stack traces automatically appear in exception logs when the DEBUG setting is enabled. In production (DEBUG=False), the log_exception utility logs the exception without full tracebacks unless explicitly configured otherwise, reducing noise while preserving error context.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →