# ADR-007: Logging Standardization Pattern ## Status Accepted ## Context As the ThrillWiki application grew, logging patterns became inconsistent across different modules. Some files had no logging, others used print statements, and logging levels were applied inconsistently. This made debugging production issues difficult and prevented effective monitoring. The team needed: - Consistent logging patterns across all modules - Structured logging with contextual information - Security event tracking for compliance - Performance monitoring capabilities - Integration with centralized logging systems (Sentry, CloudWatch) ## Decision We implemented a **standardized logging pattern** with centralized utilities for structured logging. ### Core Pattern Every module initializes a logger using Python's standard logging: ```python import logging logger = logging.getLogger(__name__) ``` ### Centralized Logging Utilities Created `apps.core.logging` module with specialized logging functions: 1. **`log_exception(logger, exception, context, request)`** - Logs exceptions with full stack trace - Includes request context (user, IP, path) - Automatically sends to Sentry in production 2. **`log_business_event(logger, event_type, message, context, request)`** - Logs significant business operations - Used for FSM transitions, user actions, data changes - Structured format for analytics 3. **`log_security_event(logger, event_type, message, severity, context, request)`** - Logs authentication and authorization events - Tracks security-relevant actions - Supports compliance auditing ### Log Levels Standardized log level usage: | Level | Usage | |-------|-------| | `DEBUG` | Detailed diagnostic information (disabled in production) | | `INFO` | General operational events (user actions, searches) | | `WARNING` | Unexpected conditions that don't prevent operation | | `ERROR` | Error conditions requiring attention | | `CRITICAL` | System failures requiring immediate action | ### Logging Configuration Modular logging configuration in `config/settings/logging.py`: ```python LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'json': { '()': 'pythonjsonlogger.jsonlogger.JsonFormatter', 'format': '%(asctime)s %(name)s %(levelname)s %(message)s', }, 'verbose': { 'format': '{levelname} {asctime} {name} {message}', 'style': '{', }, }, 'handlers': { 'console': { 'class': 'logging.StreamHandler', 'formatter': 'verbose', }, 'file': { 'class': 'logging.handlers.RotatingFileHandler', 'filename': 'logs/django.log', 'maxBytes': 10485760, # 10MB 'backupCount': 5, 'formatter': 'json', }, 'sentry': { 'class': 'sentry_sdk.integrations.logging.EventHandler', 'level': 'ERROR', }, }, 'loggers': { 'django': { 'handlers': ['console', 'file'], 'level': 'INFO', }, 'apps': { 'handlers': ['console', 'file', 'sentry'], 'level': 'INFO', 'propagate': False, }, }, } ``` ## Consequences ### Benefits 1. **Consistent Debugging**: All modules log in the same format 2. **Structured Logs**: JSON format enables log aggregation and analysis 3. **Security Auditing**: Dedicated security event logging for compliance 4. **Performance Monitoring**: Cache and query performance tracking 5. **Production Monitoring**: Integration with Sentry for error tracking 6. **Contextual Information**: All logs include user, request, and operation context ### Trade-offs 1. **Verbosity**: More logging code in each module 2. **Performance**: Logging has minimal overhead (~1-2ms per log) 3. **Storage**: JSON logs consume more disk space than plain text 4. **Learning Curve**: Developers must learn when to use each logging function ### Implementation Guidelines 1. **Logger Initialization**: Every view, service, and middleware file must initialize a logger 2. **Exception Handling**: Always use `log_exception()` in exception handlers 3. **Business Events**: Use `log_business_event()` for FSM transitions and significant actions 4. **Security Events**: Use `log_security_event()` for authentication and authorization 5. **Sensitive Data**: Never log passwords, tokens, session IDs, or PII ### Example Usage ```python import logging from apps.core.logging import log_exception, log_business_event, log_security_event logger = logging.getLogger(__name__) class ParkDetailView(DetailView): def get_object(self): try: park = super().get_object() logger.info(f"Park viewed: {park.name}", extra={ 'park_id': park.id, 'user_id': self.request.user.id if self.request.user.is_authenticated else None, }) return park except Park.DoesNotExist as e: log_exception( logger, e, context={'slug': self.kwargs.get('slug')}, request=self.request, ) raise def post(self, request, *args, **kwargs): park = self.get_object() old_status = park.status park.approve() park.save() log_business_event( logger, event_type='fsm_transition', message=f'Park approved: {park.name}', context={ 'model': 'Park', 'object_id': park.id, 'old_state': old_status, 'new_state': park.status, }, request=request, ) return redirect('park_detail', slug=park.slug) ``` ## Alternatives Considered ### Django Debug Toolbar Only **Rejected because:** - Only works in development - No production monitoring - No structured logging - No security event tracking ### Third-Party Logging Service (Datadog, New Relic) **Rejected because:** - High cost for small team - Vendor lock-in - Sentry + CloudWatch sufficient for current needs ### Print Statements **Rejected because:** - Not configurable - No log levels - No structured format - Not production-ready ## References - [Python Logging Documentation](https://docs.python.org/3/library/logging.html) - [Django Logging Documentation](https://docs.djangoproject.com/en/5.0/topics/logging/) - [Sentry Integration](https://docs.sentry.io/platforms/python/guides/django/) - [Code Standards - Logging](../../backend/docs/code_standards.md#logging-standards)