ShreyasGosavi's picture
Upload 37 files
53bec59 verified
"""
Production-Grade Structured Logging
"""
import logging
import sys
import json
from datetime import datetime
from typing import Any, Dict
from pathlib import Path
from pythonjsonlogger import jsonlogger
from .config import settings
class CustomJsonFormatter(jsonlogger.JsonFormatter):
"""Custom JSON formatter with additional fields"""
def add_fields(self, log_record: Dict[str, Any], record: logging.LogRecord, message_dict: dict):
super().add_fields(log_record, record, message_dict)
# Add timestamp
log_record['timestamp'] = datetime.utcnow().isoformat()
# Add log level
log_record['level'] = record.levelname
# Add application context
log_record['app'] = settings.APP_NAME
log_record['version'] = settings.APP_VERSION
log_record['environment'] = settings.ENVIRONMENT
# Add request ID if available (will be set by middleware)
if hasattr(record, 'request_id'):
log_record['request_id'] = record.request_id
# Add user ID if available
if hasattr(record, 'user_id'):
log_record['user_id'] = record.user_id
def setup_logging():
"""Configure application logging"""
# Create logger
logger = logging.getLogger()
logger.setLevel(getattr(logging, settings.LOG_LEVEL.upper()))
# Remove existing handlers
logger.handlers = []
# Console handler
console_handler = logging.StreamHandler(sys.stdout)
if settings.LOG_FORMAT == "json":
console_formatter = CustomJsonFormatter(
'%(timestamp)s %(level)s %(name)s %(message)s'
)
else:
console_formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
console_handler.setFormatter(console_formatter)
logger.addHandler(console_handler)
# File handler (if configured)
if settings.LOG_FILE:
log_file = Path(settings.LOG_FILE)
log_file.parent.mkdir(parents=True, exist_ok=True)
file_handler = logging.FileHandler(log_file)
if settings.LOG_FORMAT == "json":
file_formatter = CustomJsonFormatter(
'%(timestamp)s %(level)s %(name)s %(message)s'
)
else:
file_formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
file_handler.setFormatter(file_formatter)
logger.addHandler(file_handler)
return logger
# Create module-level logger
logger = setup_logging()
def log_api_request(
method: str,
path: str,
status_code: int,
duration_ms: float,
user_id: str = None,
request_id: str = None
):
"""Log API request with structured data"""
logger.info(
"API Request",
extra={
"method": method,
"path": path,
"status_code": status_code,
"duration_ms": duration_ms,
"user_id": user_id,
"request_id": request_id,
"event_type": "api_request"
}
)
def log_prediction(
model_type: str,
input_size: int,
confidence: float,
duration_ms: float,
cached: bool = False,
user_id: str = None
):
"""Log ML prediction with metrics"""
logger.info(
"ML Prediction",
extra={
"model_type": model_type,
"input_size": input_size,
"confidence": confidence,
"duration_ms": duration_ms,
"cached": cached,
"user_id": user_id,
"event_type": "prediction"
}
)
def log_error(
error: Exception,
context: Dict[str, Any] = None,
user_id: str = None,
request_id: str = None
):
"""Log error with full context"""
logger.error(
f"Error: {str(error)}",
extra={
"error_type": type(error).__name__,
"error_message": str(error),
"context": context or {},
"user_id": user_id,
"request_id": request_id,
"event_type": "error"
},
exc_info=True
)
if __name__ == "__main__":
# Test logging
logger.info("Application starting")
logger.debug("Debug message")
logger.warning("Warning message")
logger.error("Error message")
log_api_request("GET", "/api/v1/health", 200, 5.2, request_id="test-123")
log_prediction("deepfake", 1024, 0.95, 125.5, cached=False, user_id="user-1")