Spaces:
Sleeping
Sleeping
| import logging | |
| import json | |
| import time | |
| from datetime import datetime | |
| from typing import Optional, Dict, Any | |
| from functools import wraps | |
| from fastapi import Request | |
| import threading | |
| # Configure structured logging | |
| class JSONFormatter(logging.Formatter): | |
| """Custom JSON formatter for structured logging.""" | |
| def format(self, record): | |
| log_data = { | |
| '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, 'extra'): | |
| log_data.update(record.extra) | |
| return json.dumps(log_data) | |
| def setup_logging(json_format: bool = False): | |
| """Setup logging configuration.""" | |
| logger = logging.getLogger('mexar') | |
| logger.setLevel(logging.INFO) | |
| # Console handler | |
| handler = logging.StreamHandler() | |
| if json_format: | |
| handler.setFormatter(JSONFormatter()) | |
| else: | |
| handler.setFormatter(logging.Formatter( | |
| '%(asctime)s - %(name)s - %(levelname)s - %(message)s' | |
| )) | |
| logger.addHandler(handler) | |
| return logger | |
| # Analytics tracker | |
| class AnalyticsTracker: | |
| """ | |
| Simple in-memory analytics for tracking usage patterns. | |
| """ | |
| def __init__(self): | |
| self._metrics = { | |
| 'api_calls': {}, | |
| 'chat_messages': 0, | |
| 'compilations': 0, | |
| 'errors': [], | |
| 'response_times': [] | |
| } | |
| self._lock = threading.RLock() | |
| def track_api_call(self, endpoint: str, method: str, status_code: int, duration_ms: float): | |
| """Track an API call.""" | |
| with self._lock: | |
| key = f"{method}:{endpoint}" | |
| if key not in self._metrics['api_calls']: | |
| self._metrics['api_calls'][key] = { | |
| 'count': 0, | |
| 'success': 0, | |
| 'errors': 0, | |
| 'avg_duration_ms': 0 | |
| } | |
| self._metrics['api_calls'][key]['count'] += 1 | |
| if 200 <= status_code < 400: | |
| self._metrics['api_calls'][key]['success'] += 1 | |
| else: | |
| self._metrics['api_calls'][key]['errors'] += 1 | |
| # Update rolling average | |
| current = self._metrics['api_calls'][key] | |
| current['avg_duration_ms'] = ( | |
| (current['avg_duration_ms'] * (current['count'] - 1) + duration_ms) | |
| / current['count'] | |
| ) | |
| def track_chat(self): | |
| """Track a chat message.""" | |
| with self._lock: | |
| self._metrics['chat_messages'] += 1 | |
| def track_compilation(self): | |
| """Track a compilation.""" | |
| with self._lock: | |
| self._metrics['compilations'] += 1 | |
| def track_error(self, error: str, endpoint: str = None): | |
| """Track an error.""" | |
| with self._lock: | |
| self._metrics['errors'].append({ | |
| 'timestamp': datetime.utcnow().isoformat(), | |
| 'error': error, | |
| 'endpoint': endpoint | |
| }) | |
| # Keep only last 100 errors | |
| if len(self._metrics['errors']) > 100: | |
| self._metrics['errors'] = self._metrics['errors'][-100:] | |
| def get_stats(self) -> dict: | |
| """Get current analytics stats.""" | |
| with self._lock: | |
| total_calls = sum(v['count'] for v in self._metrics['api_calls'].values()) | |
| total_errors = sum(v['errors'] for v in self._metrics['api_calls'].values()) | |
| return { | |
| 'total_api_calls': total_calls, | |
| 'total_errors': total_errors, | |
| 'error_rate': total_errors / total_calls if total_calls > 0 else 0, | |
| 'chat_messages': self._metrics['chat_messages'], | |
| 'compilations': self._metrics['compilations'], | |
| 'endpoints': self._metrics['api_calls'], | |
| 'recent_errors': self._metrics['errors'][-10:] | |
| } | |
| def reset(self): | |
| """Reset all metrics.""" | |
| with self._lock: | |
| self._metrics = { | |
| 'api_calls': {}, | |
| 'chat_messages': 0, | |
| 'compilations': 0, | |
| 'errors': [], | |
| 'response_times': [] | |
| } | |
| # Singleton instance | |
| analytics = AnalyticsTracker() | |
| logger = setup_logging() | |
| # Middleware for request logging and analytics | |
| async def logging_middleware(request: Request, call_next): | |
| """Log and track all requests.""" | |
| start_time = time.time() | |
| # Process request | |
| response = await call_next(request) | |
| # Calculate duration | |
| duration_ms = (time.time() - start_time) * 1000 | |
| # Track in analytics | |
| analytics.track_api_call( | |
| endpoint=request.url.path, | |
| method=request.method, | |
| status_code=response.status_code, | |
| duration_ms=duration_ms | |
| ) | |
| # Log request | |
| logger.info( | |
| f"{request.method} {request.url.path} - {response.status_code} - {duration_ms:.2f}ms" | |
| ) | |
| return response | |
| # Decorator for function-level logging | |
| def log_function(func): | |
| """Decorator to log function calls.""" | |
| def wrapper(*args, **kwargs): | |
| logger.info(f"Calling {func.__name__}") | |
| try: | |
| result = func(*args, **kwargs) | |
| logger.info(f"{func.__name__} completed successfully") | |
| return result | |
| except Exception as e: | |
| logger.error(f"{func.__name__} failed: {str(e)}") | |
| analytics.track_error(str(e)) | |
| raise | |
| return wrapper | |
| async def async_log_function(func): | |
| """Decorator for async function logging.""" | |
| async def wrapper(*args, **kwargs): | |
| logger.info(f"Calling {func.__name__}") | |
| try: | |
| result = await func(*args, **kwargs) | |
| logger.info(f"{func.__name__} completed successfully") | |
| return result | |
| except Exception as e: | |
| logger.error(f"{func.__name__} failed: {str(e)}") | |
| analytics.track_error(str(e)) | |
| raise | |
| return wrapper | |