import os import sys from enum import Enum from logging import Formatter, LogRecord, getLogger, StreamHandler, FileHandler from pathlib import Path from typing import Optional from colorama import Fore, Style class LoggerTarget(Enum): CONSOLE = "console" FILE = "file" BOTH = "both" class ColorFormatter(Formatter): COLORS_LEVEL = { "DEBUG": Fore.CYAN, "INFO": Fore.GREEN, "WARNING": Fore.YELLOW, "ERROR": Fore.RED, "CRITICAL": Fore.MAGENTA + Style.BRIGHT, } def format(self, record: LogRecord): original_levelname = record.levelname color = self.COLORS_LEVEL.get(original_levelname, "") record.levelname = f"{color}{original_levelname}{Style.RESET_ALL}" formatted = super().format(record) return formatted class CoreLogger: def __init__( self, name: str = "app", level: str = "INFO", fmt: Optional[str] = None, datefmt: Optional[str] = None, log_to_console: bool = True, log_to_file: bool = False, ): self.logger = getLogger(name) self.logger.setLevel(level) self.log_to_console = log_to_console self.log_to_file = log_to_file self.logger.propagate = False self.fmt = fmt or "%(asctime)s | %(levelname)-8s | %(message)s" self.datefmt = datefmt or "%Y-%m-%d %H:%M:%S" self.logger_file_path = Path(__file__).resolve().parents[2] self.logger_file_path = self.logger_file_path / "logs" / "app.log" formatter = Formatter(self.fmt, self.datefmt) color_formatter = ColorFormatter(self.fmt, self.datefmt) if self.log_to_console: console_handler = StreamHandler(sys.stdout) console_handler.setFormatter(color_formatter) self.logger.addHandler(console_handler) if self.log_to_file: log_file_path = Path(self.logger_file_path).resolve() log_file_path.parent.mkdir(parents=True, exist_ok=True) fh = FileHandler(log_file_path, encoding="utf-8") fh.setFormatter(formatter) self.logger.addHandler(fh) def debug(self, message: str): self.logger.debug(message) def info(self, message: str): self.logger.info(message) def warning(self, message: str): self.logger.warning(message) def error(self, message: str): self.logger.error(message) def clear_log(self, target: LoggerTarget = LoggerTarget.BOTH): if target in (LoggerTarget.CONSOLE, LoggerTarget.BOTH) and self.log_to_console: os.system("cls" if sys.platform.startswith("win") else "clear") if target in (LoggerTarget.FILE, LoggerTarget.BOTH) and self.log_to_file: log_file_path = Path(self.logger_file_path).resolve() if log_file_path.exists(): with log_file_path.open("w", encoding="utf-8") as f: f.truncate(0) if __name__ == "__main__": logger = CoreLogger(log_to_console=True, log_to_file=True) logger.info("test")