File size: 2,181 Bytes
f5628ad
2868f80
 
f5628ad
 
 
25ed685
 
 
 
 
 
2868f80
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7dac988
2868f80
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f5628ad
 
 
 
 
 
 
 
 
 
25ed685
 
 
 
 
 
 
 
 
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import logging
import logging.handlers
import os
import yaml
from pathlib import Path

try:
    import psutil as _psutil
    _PSUTIL = True
except ImportError:
    _PSUTIL = False

_logging_configured = False


def configure_logging() -> None:
    """Configure root logger once: console + rotating file. Call from main.py only."""
    global _logging_configured
    if _logging_configured:
        return

    log_dir = Path("logs")
    log_dir.mkdir(exist_ok=True)

    fmt = logging.Formatter(
        "%(asctime)s  %(levelname)-8s  %(name)-30s  %(message)s",
        datefmt="%Y-%m-%d %H:%M:%S",
    )

    console = logging.StreamHandler()
    console.setFormatter(fmt)

    file_handler = logging.handlers.RotatingFileHandler(
        log_dir / "prism.log",
        maxBytes=5 * 1024 * 1024,  # 5 MB
        backupCount=3,
        encoding="utf-8",
    )
    file_handler.setFormatter(fmt)

    level = getattr(logging, os.getenv("LOG_LEVEL", "INFO").upper(), logging.INFO)
    root = logging.getLogger()
    root.setLevel(level)
    root.addHandler(console)
    root.addHandler(file_handler)

    # Quiet noisy third-party loggers
    for noisy in (
        "httpx", "httpcore", "chromadb", "urllib3", "multipart",
        "huggingface_hub", "sentence_transformers", "transformers",
    ):
        logging.getLogger(noisy).setLevel(logging.WARNING)

    _logging_configured = True


def setup_logger(name: str) -> logging.Logger:
    """Return named logger. configure_logging() must be called before first use."""
    return logging.getLogger(name)


def load_config(config_path: str = "config.yaml") -> dict:
    """Load config.yaml and return as dict."""
    with open(config_path, "r") as f:
        return yaml.safe_load(f)


def count_tokens(text: str) -> int:
    """Approximate token count: len(text.split()) * 1.3"""
    return int(len(text.split()) * 1.3)


def log_memory_mb(logger: logging.Logger, label: str) -> float:
    """Log current process RSS in MB. Returns MB (0 if psutil unavailable)."""
    if not _PSUTIL:
        return 0.0
    rss = _psutil.Process().memory_info().rss / 1024 / 1024
    logger.info("MEM [%s] RSS=%.1fMB", label, rss)
    return rss