import json import logging import logging.handlers from datetime import datetime, timezone from rich.logging import RichHandler class JSONFormatter(logging.Formatter): def format(self, record): log_entry = { "timestamp": datetime.now(timezone.utc).isoformat(), "level": record.levelname, "logger": record.name, "message": record.getMessage(), "module": record.module, "function": record.funcName, "line": record.lineno, } standard_keys = set(logging.LogRecord("", 0, "", 0, "", (), None).__dict__.keys()) for key, value in record.__dict__.items(): if key not in standard_keys and key not in ("message", "msg", "args"): log_entry[key] = value if record.exc_info and record.exc_info[0]: log_entry["exception"] = self.formatException(record.exc_info) return json.dumps(log_entry, default=str, ensure_ascii=False) def setup_logging(level=logging.INFO, log_file=None): for lib_name in ("urllib3", "requests", "http.client", "markdown", "Markdown", "markdown_it"): logging.getLogger(lib_name).setLevel(logging.WARNING) handlers = [ RichHandler( rich_tracebacks=True, show_path=False, log_time_format="[%H:%M:%S]", markup=True, ) ] if log_file: file_handler = logging.handlers.RotatingFileHandler( log_file, maxBytes=10_000_000, backupCount=3, encoding="utf-8" ) file_handler.setFormatter(JSONFormatter()) handlers.append(file_handler) logging.basicConfig( level=level, format="%(message)s", datefmt="[%X]", handlers=handlers, ) if level == logging.DEBUG: logging.getLogger(__name__).debug("[dim]Debug mode active.[/dim]")