remodulation du projet et des classes
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
"""Structured logging configuration for the Team Tryouts application.
|
||||
|
||||
This module configures rotating file handlers for application logs,
|
||||
with separate files for errors, authentication events, and general logs.
|
||||
Sensitive data (passwords, tokens) is automatically filtered out.
|
||||
|
||||
Usage:
|
||||
from logging_config import configure_logging
|
||||
configure_logging(app)
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
from logging.handlers import RotatingFileHandler
|
||||
import re
|
||||
|
||||
|
||||
class SensitiveDataFilter(logging.Filter):
|
||||
"""Logging filter that redacts sensitive information from log messages.
|
||||
|
||||
Filters out: passwords, API keys, session tokens, and other secrets
|
||||
that might accidentally be logged.
|
||||
"""
|
||||
|
||||
# Patterns to redact
|
||||
SENSITIVE_PATTERNS = [
|
||||
(re.compile(r'(?:password|passwd|secret|token|api[_-]?key)\s*[:=]\s*[^\s,;)]+', re.IGNORECASE), '[REDACTED]'),
|
||||
(re.compile(r'(?:password|passwd|secret|token|api[_-]?key)\s*[:=]\s*"[^"]*"', re.IGNORECASE), lambda m: m.group(0).split('=')[0] + '="[REDACTED]"'),
|
||||
(re.compile(r'Authorization[:\s]+[^\s]+', re.IGNORECASE), 'Authorization: [REDACTED]'),
|
||||
(re.compile(r'Bearer\s+[^\s]+', re.IGNORECASE), 'Bearer [REDACTED]'),
|
||||
]
|
||||
|
||||
def filter(self, record):
|
||||
"""Apply redaction to the log record's message.
|
||||
|
||||
Args:
|
||||
record: The log record to filter.
|
||||
|
||||
Returns:
|
||||
bool: Always True (never drops records, only redacts).
|
||||
"""
|
||||
if hasattr(record, 'msg') and isinstance(record.msg, str):
|
||||
msg = record.msg
|
||||
for pattern, replacement in self.SENSITIVE_PATTERNS:
|
||||
if callable(replacement):
|
||||
msg = pattern.sub(replacement, msg)
|
||||
else:
|
||||
msg = pattern.sub(replacement, msg)
|
||||
record.msg = msg
|
||||
return True
|
||||
|
||||
|
||||
def configure_logging(app):
|
||||
"""Configure structured logging for the Flask application.
|
||||
|
||||
Sets up three rotating file handlers:
|
||||
- errors.log: ERROR and CRITICAL level messages
|
||||
- auth.log: Authentication-related events (INFO and above)
|
||||
- app.log: All application logs (DEBUG and above, configurable)
|
||||
|
||||
Also configures console output for development.
|
||||
|
||||
Args:
|
||||
app: The Flask application instance to configure logging for.
|
||||
"""
|
||||
log_dir = os.path.join(os.getcwd(), 'logs')
|
||||
os.makedirs(log_dir, exist_ok=True)
|
||||
|
||||
# Remove default Flask handlers to avoid duplicate logging
|
||||
app.logger.handlers.clear()
|
||||
|
||||
# Set base log level from environment (default: INFO)
|
||||
log_level_name = os.getenv('LOG_LEVEL', 'INFO').upper()
|
||||
log_level = getattr(logging, log_level_name, logging.INFO)
|
||||
app.logger.setLevel(log_level)
|
||||
|
||||
# Create the sensitive data filter
|
||||
sensitive_filter = SensitiveDataFilter()
|
||||
|
||||
# Formatter with timestamp, level, module, and message
|
||||
formatter = logging.Formatter(
|
||||
'[%(asctime)s] %(levelname)s [%(name)s:%(lineno)d] %(message)s',
|
||||
datefmt='%Y-%m-%d %H:%M:%S'
|
||||
)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# 1. Error Log Handler
|
||||
# -------------------------------------------------------------------------
|
||||
error_handler = RotatingFileHandler(
|
||||
os.path.join(log_dir, 'errors.log'),
|
||||
maxBytes=10 * 1024 * 1024, # 10 MB
|
||||
backupCount=10
|
||||
)
|
||||
error_handler.setLevel(logging.ERROR)
|
||||
error_handler.setFormatter(formatter)
|
||||
error_handler.addFilter(sensitive_filter)
|
||||
app.logger.addHandler(error_handler)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# 2. Authentication Log Handler
|
||||
# -------------------------------------------------------------------------
|
||||
auth_handler = RotatingFileHandler(
|
||||
os.path.join(log_dir, 'auth.log'),
|
||||
maxBytes=10 * 1024 * 1024, # 10 MB
|
||||
backupCount=5
|
||||
)
|
||||
auth_handler.setLevel(logging.INFO)
|
||||
auth_handler.setFormatter(formatter)
|
||||
auth_handler.addFilter(sensitive_filter)
|
||||
|
||||
# Create a named logger specifically for auth events
|
||||
auth_logger = logging.getLogger('team_tryouts.auth')
|
||||
auth_logger.setLevel(logging.INFO)
|
||||
auth_logger.addHandler(auth_handler)
|
||||
auth_logger.propagate = False # Don't double-log to root
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# 3. Application Log Handler (general)
|
||||
# -------------------------------------------------------------------------
|
||||
app_handler = RotatingFileHandler(
|
||||
os.path.join(log_dir, 'app.log'),
|
||||
maxBytes=10 * 1024 * 1024, # 10 MB
|
||||
backupCount=10
|
||||
)
|
||||
app_handler.setLevel(log_level)
|
||||
app_handler.setFormatter(formatter)
|
||||
app_handler.addFilter(sensitive_filter)
|
||||
app.logger.addHandler(app_handler)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# 4. Console Handler (for development)
|
||||
# -------------------------------------------------------------------------
|
||||
if os.getenv('FLASK_DEBUG', 'false').lower() == 'true':
|
||||
console_handler = logging.StreamHandler()
|
||||
console_handler.setLevel(logging.DEBUG)
|
||||
console_handler.setFormatter(formatter)
|
||||
console_handler.addFilter(sensitive_filter)
|
||||
app.logger.addHandler(console_handler)
|
||||
|
||||
# Log startup information
|
||||
app.logger.info('Logging configured - Level: %s, Log directory: %s', log_level_name, log_dir)
|
||||
app.logger.info('Application startup')
|
||||
|
||||
return app.logger
|
||||
|
||||
|
||||
# Module-level auth logger factory
|
||||
def get_auth_logger():
|
||||
"""Get the authentication event logger.
|
||||
|
||||
Returns:
|
||||
logging.Logger: Logger for authentication events.
|
||||
"""
|
||||
return logging.getLogger('team_tryouts.auth')
|
||||
Reference in New Issue
Block a user