Files
ebook-extension/ebook_backend&admin_panel/admin-backend/utils/logger.py

157 lines
5.3 KiB
Python

"""
Professional logging utility for the Ebook Coupon Management System
Provides structured logging with proper formatting and log levels.
"""
import logging
import logging.handlers
import os
import sys
from datetime import datetime
from typing import Optional, Any, Dict
import json
class SafeJSONEncoder(json.JSONEncoder):
"""Custom JSON encoder that handles non-serializable objects safely"""
def default(self, obj):
"""Handle non-serializable objects by converting them to strings"""
if hasattr(obj, '__dict__'):
return str(obj)
elif hasattr(obj, '__str__'):
return str(obj)
else:
return f"<{type(obj).__name__} object>"
class StructuredFormatter(logging.Formatter):
"""Custom formatter for structured logging"""
def format(self, record: logging.LogRecord) -> str:
"""Format log record with structured data"""
log_entry = {
"timestamp": datetime.utcnow().isoformat(),
"level": record.levelname,
"logger": record.name,
"message": record.getMessage(),
"module": record.module,
"function": record.funcName,
"line": record.lineno
}
# Add extra fields if present
if hasattr(record, 'request_id'):
log_entry['request_id'] = record.request_id
if hasattr(record, 'method'):
log_entry['method'] = record.method
if hasattr(record, 'path'):
log_entry['path'] = record.path
if hasattr(record, 'status_code'):
log_entry['status_code'] = record.status_code
if hasattr(record, 'process_time'):
log_entry['process_time'] = record.process_time
if hasattr(record, 'client_ip'):
log_entry['client_ip'] = record.client_ip
if hasattr(record, 'user_agent'):
log_entry['user_agent'] = record.user_agent
if hasattr(record, 'error'):
log_entry['error'] = record.error
if hasattr(record, 'exception_type'):
log_entry['exception_type'] = record.exception_type
if hasattr(record, 'exception_message'):
log_entry['exception_message'] = record.exception_message
if hasattr(record, 'errors'):
# Handle errors list safely
try:
if isinstance(record.errors, list):
log_entry['errors'] = [str(error) if not isinstance(error, (dict, str, int, float, bool)) else error for error in record.errors]
else:
log_entry['errors'] = str(record.errors)
except Exception:
log_entry['errors'] = str(record.errors)
if hasattr(record, 'app_name'):
log_entry['app_name'] = record.app_name
if hasattr(record, 'version'):
log_entry['version'] = record.version
if hasattr(record, 'environment'):
log_entry['environment'] = record.environment
if hasattr(record, 'debug'):
log_entry['debug'] = record.debug
# Add exception info if present
if record.exc_info:
log_entry['exception'] = self.formatException(record.exc_info)
return json.dumps(log_entry, ensure_ascii=False, cls=SafeJSONEncoder)
def setup_logger(name: str, level: Optional[str] = None) -> logging.Logger:
"""
Setup a logger with proper configuration
Args:
name: Logger name
level: Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
Returns:
Configured logger instance
"""
# Get log level from environment or use default
log_level = level or os.getenv("LOG_LEVEL", "INFO").upper()
# Create logger
logger = logging.getLogger(name)
logger.setLevel(getattr(logging, log_level))
# Avoid duplicate handlers
if logger.handlers:
return logger
# Create formatters
structured_formatter = StructuredFormatter()
console_formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# Console handler
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(logging.DEBUG)
console_handler.setFormatter(console_formatter)
logger.addHandler(console_handler)
# File handler for structured logs
log_dir = "logs"
os.makedirs(log_dir, exist_ok=True)
file_handler = logging.handlers.RotatingFileHandler(
os.path.join(log_dir, "app.log"),
maxBytes=10*1024*1024, # 10MB
backupCount=5
)
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(structured_formatter)
logger.addHandler(file_handler)
# Error file handler
error_handler = logging.handlers.RotatingFileHandler(
os.path.join(log_dir, "error.log"),
maxBytes=10*1024*1024, # 10MB
backupCount=5
)
error_handler.setLevel(logging.ERROR)
error_handler.setFormatter(structured_formatter)
logger.addHandler(error_handler)
return logger
def get_logger(name: str) -> logging.Logger:
"""
Get a logger instance
Args:
name: Logger name
Returns:
Logger instance
"""
return logging.getLogger(name)
# Create default logger
default_logger = setup_logger("ebook_coupon_system")