File size: 2,166 Bytes
bdef324
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# logger.py — In-memory log handler for realtime log streaming
# Ported from Daily Scrapper architecture

import logging
from datetime import datetime
from typing import List, Dict, Optional


class MemoryLogHandler(logging.Handler):
    """Stores log entries in memory for API consumption (realtime 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()


# Singleton handler shared across all modules
memory_handler = MemoryLogHandler()


def get_app_logger(name: str) -> logging.Logger:
    """Get a named logger that writes to both console and memory buffer."""
    logger = logging.getLogger(f"app.{name}")
    logger.setLevel(logging.INFO)
    # Ensure memory handler is added only once
    if not any(isinstance(h, MemoryLogHandler) for h in logger.handlers):
        logger.addHandler(memory_handler)
    # Also add console handler for local debugging
    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] %(name)s: %(message)s"))
        logger.addHandler(console)
    return logger