Skip to Content
FeaturesModulesLogging

Django Logging

Django-CFG’s Logging Module provides auto-configuring structured logging for the django_cfg namespace. It sets up modular per-module log files with daily rotation and optionally persists WARNING-and-above events to Cloudflare D1 for centralized storage, deduplication, and analysis.

Features

  • Modular log files — separate rotating file per django_cfg.* logger under logs/djangocfg/
  • Daily rotationTimedRotatingFileHandler with midnight rotation and configurable retention
  • Debug-aware levelsDEBUG to files in dev mode, WARNING to console in production
  • D1 persistence — optional sync of WARNING+ events to Cloudflare D1 with upsert-increment deduplication
  • Fingerprint deduplication — repeated occurrences increment a counter instead of creating new rows
  • Sanitized extra context — reserved LogRecord attributes in extra={} are automatically prefixed with ctx_
  • Telegram alerts — optional real-time error notifications (when django_telegram is configured)

Quick Start

from django_cfg.modules.django_logging import get_logger logger = get_logger("django_cfg.myapp") logger.debug("Processing started") logger.info("User created", extra={"user_id": 42}) logger.warning("Rate limit approaching", extra={"endpoint": "/api/users"}) logger.error("Payment failed", extra={"order_id": "ORD-123"})

get_logger() automatically detects the caller’s file path to build the logger name when called without arguments from within django_cfg code:

# In a file at django_cfg/modules/my_module/service.py logger = get_logger() # auto-resolves to: django_cfg.my_module (or similar based on path) # Explicit name logger = get_logger("django_cfg.payments.stripe")

Configuration

Configure in your extension’s __cfg__.py or on the DjangoConfig:

from django_cfg.modules.django_logging import DjangoLoggingConfig class MyExtensionSettings: logging = DjangoLoggingConfig( enabled=True, d1_enabled=True, d1_min_level="WARNING", file_enabled=True, file_rotation_days=30, telegram_alerts_enabled=False, cleanup_days=90, normalization_enabled=True, )

DjangoLoggingConfig Fields

FieldTypeDefaultDescription
enabledboolTrueEnable or disable the module
d1_enabledboolTrueSync events to Cloudflare D1 (requires django_cf)
d1_min_levelstr"WARNING"Minimum level for D1 sync: WARNING, ERROR, CRITICAL
file_enabledboolTrueEnable file logging
file_rotation_daysint30Days of log files to retain (1–365)
telegram_alerts_enabledboolFalseSend Telegram alerts on ERROR+
cleanup_daysint90D1 event TTL in days (1–365)
normalization_enabledboolTrueStrip dynamic values before computing fingerprints

Log File Layout

On startup, DjangoLogger creates the following directory structure in the project root:

logs/ ├── django.log # All Django logs (rotation: daily, keep 30 days) └── djangocfg/ ├── core.log # django_cfg.core.* ├── email.log # django_cfg.email.* ├── telegram.log # django_cfg.telegram.* └── <module>.log # One file per django_cfg.* sub-namespace /tmp/djangocfg/ └── debug.log # Always-on debug mirror (set DJANGO_LOG_TO_TMP=false to disable)

Log level per handler:

HandlerDev (DEBUG=True)Production
ConsoleDEBUGWARNING
logs/django.logDEBUGINFO
logs/djangocfg/<module>.logDEBUGINFO
/tmp/djangocfg/debug.logDEBUGDEBUG

Public API

get_logger(name="")

Returns a configured logging.Logger instance. Auto-detects caller path when called without arguments from within django_cfg code.

from django_cfg.modules.django_logging import get_logger logger = get_logger("django_cfg.payments")

sanitize_extra(extra)

Prefixes any key in extra that conflicts with Python’s reserved LogRecord attributes (e.g., module, message, name) with ctx_ to prevent KeyError.

from django_cfg.modules.django_logging import sanitize_extra safe = sanitize_extra({"module": "myapp", "user_id": 42}) # {"ctx_module": "myapp", "user_id": 42} logger.info("Event", extra=safe)

clean_old_logs(days=30, logs_dir=None)

Deletes log files older than days from the logs/ directory. Returns a stats dict:

from django_cfg.modules.django_logging import clean_old_logs stats = clean_old_logs(days=7) # {"deleted": 12, "bytes": 45678, "human_readable": "0.04 MB"}

is_enabled()

Returns True if D1 persistence is active (d1_enabled=True and django_cf is ready):

from django_cfg.modules.django_logging import is_enabled if is_enabled(): print("D1 log sync is active")

get_service() / reset_service()

Lifecycle management for the LogSyncService (D1 persistence backend). reset_service() clears the cached instance and is intended for use in tests.

D1 Persistence

When d1_enabled=True and the django_cf module is configured with Cloudflare credentials, log events at d1_min_level and above are pushed to a D1 log_events table via LogSyncService.push_log_event().

The D1 schema uses fingerprint + api_url as the composite primary key. Repeated events with the same fingerprint increment occurrence_count and update last_seen rather than creating duplicate rows.

D1 table columns:

ColumnTypeDescription
fingerprintTEXT (PK)Hash of normalized message + logger + level
api_urlTEXT (PK)Source API URL for multi-tenant isolation
levelTEXTLog level name
logger_nameTEXTLogger name
messageTEXTLog message
moduleTEXTSource module filename
func_nameTEXTSource function name
stack_traceTEXTException traceback
extraTEXT (JSON)Extra context dict
occurrence_countINTEGERNumber of times this fingerprint was seen
is_resolvedINTEGERWhether the issue has been marked resolved
first_seenTEXTISO timestamp of first occurrence
last_seenTEXTISO timestamp of most recent occurrence

Streamlit Log Viewer

When the Streamlit admin panel is enabled, a log events page is available at the “Log Events” tab. It queries the D1 log_events table and provides filtering by level, logger name, and resolved status.

Environment Variable

Set DJANGO_LOG_TO_TMP=false to disable the /tmp/djangocfg/debug.log mirror:

DJANGO_LOG_TO_TMP=false python manage.py runserver

See Also

Last updated on