Spaces:
Paused
Paused
| """ | |
| Centralized Logging System for Flare Platform | |
| """ | |
| import sys | |
| import logging | |
| import json | |
| import os | |
| import threading | |
| import traceback | |
| from datetime import datetime | |
| from enum import Enum | |
| from typing import Optional, Dict, Any, Union | |
| from pathlib import Path | |
| class LogLevel(Enum): | |
| DEBUG = "DEBUG" | |
| INFO = "INFO" | |
| WARNING = "WARNING" | |
| ERROR = "ERROR" | |
| CRITICAL = "CRITICAL" | |
| class FlareLogger: | |
| _instance = None | |
| _lock = threading.Lock() | |
| def __new__(cls): | |
| if cls._instance is None: | |
| with cls._lock: | |
| if cls._instance is None: | |
| cls._instance = super().__new__(cls) | |
| cls._instance._initialized = False | |
| return cls._instance | |
| def __init__(self): | |
| if self._initialized: | |
| return | |
| self._initialized = True | |
| # Log level from environment | |
| self.log_level = LogLevel[os.getenv('LOG_LEVEL', 'INFO')] | |
| # Configure Python logging | |
| self.logger = logging.getLogger('flare') | |
| self.logger.setLevel(self.log_level.value) | |
| # Remove default handlers | |
| self.logger.handlers = [] | |
| # Console handler with custom format | |
| console_handler = logging.StreamHandler(sys.stdout) | |
| console_handler.setFormatter(self._get_formatter()) | |
| self.logger.addHandler(console_handler) | |
| # File handler for production | |
| if os.getenv('LOG_TO_FILE', 'false').lower() == 'true': | |
| log_dir = Path('logs') | |
| log_dir.mkdir(exist_ok=True) | |
| file_handler = logging.FileHandler( | |
| log_dir / f"flare_{datetime.now().strftime('%Y%m%d')}.log" | |
| ) | |
| file_handler.setFormatter(self._get_formatter()) | |
| self.logger.addHandler(file_handler) | |
| # Future: Add ElasticSearch handler here | |
| # if os.getenv('ELASTICSEARCH_URL'): | |
| # from elasticsearch_handler import ElasticsearchHandler | |
| # es_handler = ElasticsearchHandler( | |
| # hosts=[os.getenv('ELASTICSEARCH_URL')], | |
| # index='flare-logs' | |
| # ) | |
| # self.logger.addHandler(es_handler) | |
| def _get_formatter(self): | |
| return logging.Formatter( | |
| '[%(asctime)s.%(msecs)03d] [%(levelname)s] [%(name)s] %(message)s', | |
| datefmt='%H:%M:%S' | |
| ) | |
| def log(self, level: LogLevel, message: str, **kwargs): | |
| """Central logging method with structured data""" | |
| # Add context data | |
| extra_data = { | |
| 'timestamp': datetime.utcnow().isoformat(), | |
| 'service': 'flare', | |
| 'thread_id': threading.get_ident(), | |
| **kwargs | |
| } | |
| # Log with structured data | |
| log_message = message | |
| if kwargs: | |
| # Format kwargs for readability | |
| kwargs_str = json.dumps(kwargs, ensure_ascii=False, default=str) | |
| log_message = f"{message} | {kwargs_str}" | |
| getattr(self.logger, level.value.lower())(log_message, extra={'data': extra_data}) | |
| # Always flush for real-time debugging | |
| sys.stdout.flush() | |
| def debug(self, message: str, **kwargs): | |
| """Log debug message""" | |
| self.log(LogLevel.DEBUG, message, **kwargs) | |
| def info(self, message: str, **kwargs): | |
| """Log info message""" | |
| self.log(LogLevel.INFO, message, **kwargs) | |
| def warning(self, message: str, **kwargs): | |
| """Log warning message""" | |
| self.log(LogLevel.WARNING, message, **kwargs) | |
| def error(self, message: str, **kwargs): | |
| """Log error message""" | |
| self.log(LogLevel.ERROR, message, **kwargs) | |
| def critical(self, message: str, **kwargs): | |
| """Log critical message""" | |
| self.log(LogLevel.CRITICAL, message, **kwargs) | |
| def set_level(self, level: str): | |
| """Dynamically change log level""" | |
| try: | |
| self.log_level = LogLevel[level.upper()] | |
| self.logger.setLevel(self.log_level.value) | |
| self.info(f"Log level changed to {level}") | |
| except KeyError: | |
| self.warning(f"Invalid log level: {level}") | |
| # Global logger instance | |
| logger = FlareLogger() | |
| # Convenience functions | |
| def log_debug(message: str, **kwargs): | |
| """Log debug message""" | |
| logger.debug(message, **kwargs) | |
| def log_info(message: str, **kwargs): | |
| """Log info message""" | |
| logger.info(message, **kwargs) | |
| def log_warning(message: str, **kwargs): | |
| """Log warning message""" | |
| logger.warning(message, **kwargs) | |
| def log_error(message: str, exception: Optional[Exception] = None, **kwargs): | |
| """ | |
| Log error message with optional exception | |
| Usage: | |
| log_error("Error occurred") | |
| log_error("Error occurred", e) # Otomatik olarak str(e) ve traceback ekler | |
| log_error("Error occurred", error="custom error") | |
| log_error("Error occurred", e, extra_field="value") | |
| """ | |
| import traceback | |
| # Eğer exception parametresi verilmişse, otomatik olarak error ve traceback ekle | |
| if exception is not None: | |
| # Eğer kwargs'da error yoksa, exception'dan al | |
| if 'error' not in kwargs: | |
| kwargs['error'] = str(exception) | |
| # Exception tipini ekle | |
| if 'error_type' not in kwargs: | |
| kwargs['error_type'] = type(exception).__name__ | |
| # Eğer kwargs'da traceback yoksa ve bu bir Exception ise, traceback ekle | |
| if 'traceback' not in kwargs and isinstance(exception, Exception): | |
| kwargs['traceback'] = traceback.format_exc() | |
| # Özel exception tipleri için ekstra bilgi | |
| if hasattr(exception, '__dict__'): | |
| # Custom exception'ların attribute'larını ekle | |
| for attr, value in exception.__dict__.items(): | |
| if not attr.startswith('_') and attr not in kwargs: | |
| kwargs[f'exc_{attr}'] = value | |
| # HTTP status code varsa ekle | |
| if hasattr(exception, 'status_code') and 'status_code' not in kwargs: | |
| kwargs['status_code'] = exception.status_code | |
| # Orijinal logger'a gönder | |
| logger.error(message, **kwargs) | |
| def log_critical(message: str, **kwargs): | |
| """Log critical message""" | |
| logger.critical(message, **kwargs) | |
| # Backward compatibility | |
| def log(message: str, level: str = "INFO", **kwargs): | |
| """Legacy log function for compatibility""" | |
| getattr(logger, level.lower())(message, **kwargs) | |
| # Performance logging helpers | |
| class LogTimer: | |
| """Context manager for timing operations""" | |
| def __init__(self, operation_name: str, **extra_kwargs): | |
| self.operation_name = operation_name | |
| self.extra_kwargs = extra_kwargs | |
| self.start_time = None | |
| def __enter__(self): | |
| self.start_time = datetime.now() | |
| log_debug(f"Starting {self.operation_name}", **self.extra_kwargs) | |
| return self | |
| def __exit__(self, exc_type, exc_val, exc_tb): | |
| duration_ms = (datetime.now() - self.start_time).total_seconds() * 1000 | |
| if exc_type: | |
| log_error( | |
| f"{self.operation_name} failed after {duration_ms:.2f}ms", | |
| error=str(exc_val), | |
| duration_ms=duration_ms, | |
| **self.extra_kwargs | |
| ) | |
| else: | |
| log_info( | |
| f"{self.operation_name} completed in {duration_ms:.2f}ms", | |
| duration_ms=duration_ms, | |
| **self.extra_kwargs | |
| ) | |