Spaces:
Sleeping
Sleeping
anoderb
feat: integrate machine learning forecasting pipeline and unify settings & dashboard under Sikomo theme
2860429 | # logger.py — In-memory log handler for scraper and forecast log separation | |
| import logging | |
| from datetime import datetime | |
| from typing import List, Dict, Optional | |
| class MemoryLogHandler(logging.Handler): | |
| """Stores log entries in memory for API log streaming.""" | |
| def __init__(self, capacity: int = 500): | |
| super().__init__() | |
| self.capacity = capacity | |
| self.logs: List[Dict] = [] | |
| def emit(self, record): | |
| try: | |
| msg = record.getMessage() | |
| log_entry = { | |
| "timestamp": datetime.fromtimestamp(record.created).isoformat(), | |
| "level": record.levelname, | |
| "source": record.name.split('.')[-1], | |
| "message": msg | |
| } | |
| self.logs.append(log_entry) | |
| if len(self.logs) > self.capacity: | |
| self.logs.pop(0) | |
| except Exception: | |
| self.handleError(record) | |
| def get_logs(self, limit: int = 100, level: Optional[str] = None) -> List[Dict]: | |
| filtered = self.logs | |
| if level and level.upper() != "ALL": | |
| filtered = [l for l in filtered if l["level"] == level.upper()] | |
| return filtered[-limit:] | |
| def clear_logs(self): | |
| self.logs.clear() | |
| # Separated handlers | |
| scraper_memory_handler = MemoryLogHandler(capacity=500) | |
| forecast_memory_handler = MemoryLogHandler(capacity=500) | |
| def get_app_logger(name: str) -> logging.Logger: | |
| """Default app logger for scraping operations.""" | |
| logger = logging.getLogger(f"app.scraper.{name}") | |
| logger.setLevel(logging.INFO) | |
| # Add memory handler | |
| if not any(h is scraper_memory_handler for h in logger.handlers): | |
| logger.addHandler(scraper_memory_handler) | |
| # Add console stream handler | |
| if not any(isinstance(h, logging.StreamHandler) and not isinstance(h, MemoryLogHandler) for h in logger.handlers): | |
| console = logging.StreamHandler() | |
| console.setLevel(logging.INFO) | |
| console.setFormatter(logging.Formatter("%(asctime)s [%(levelname)s] scraper.%(name)s: %(message)s")) | |
| logger.addHandler(console) | |
| return logger | |
| def get_forecast_logger(name: str) -> logging.Logger: | |
| """Dedicated app logger for forecasting operations.""" | |
| logger = logging.getLogger(f"app.forecast.{name}") | |
| logger.setLevel(logging.INFO) | |
| # Add memory handler | |
| if not any(h is forecast_memory_handler for h in logger.handlers): | |
| logger.addHandler(forecast_memory_handler) | |
| # Add console stream handler | |
| if not any(isinstance(h, logging.StreamHandler) and not isinstance(h, MemoryLogHandler) for h in logger.handlers): | |
| console = logging.StreamHandler() | |
| console.setLevel(logging.INFO) | |
| console.setFormatter(logging.Formatter("%(asctime)s [%(levelname)s] forecast.%(name)s: %(message)s")) | |
| logger.addHandler(console) | |
| return logger | |