| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- #!/usr/bin/env python3
-
- import sys
- import time
- from enum import Enum
-
-
- class LogLevel(Enum):
- """Log levels with associated colors and symbols."""
-
- DEBUG = ("🔍", "\033[90m") # Gray
- INFO = ("ℹ️ ", "\033[94m") # Blue
- SUCCESS = ("✅", "\033[92m") # Green
- WARNING = ("⚠️ ", "\033[93m") # Yellow
- ERROR = ("❌", "\033[91m") # Red
- STEP = ("🚀", "\033[96m") # Cyan
- PROGRESS = ("📋", "\033[95m") # Magenta
-
-
- class Logger:
- """Logger class for formatted console output."""
-
- def __init__(self, name: str | None = None, use_colors: bool = True):
- """Initialize logger.
-
- Args:
- name: Optional name for the logger (e.g., script name)
- use_colors: Whether to use ANSI color codes
- """
- self.name = name
- self.use_colors = use_colors and sys.stdout.isatty()
- self._reset_color = "\033[0m" if self.use_colors else ""
-
- def _format_message(self, level: LogLevel, message: str, indent: int = 0) -> str:
- """Format a log message with level, color, and indentation.
-
- Args:
- level: Log level
- message: Message to log
- indent: Number of spaces to indent
-
- Returns:
- Formatted message string
- """
- symbol, color = level.value
- color = color if self.use_colors else ""
- reset = self._reset_color
-
- prefix = " " * indent
-
- if self.name and level in [LogLevel.STEP, LogLevel.ERROR]:
- return f"{prefix}{color}{symbol} [{self.name}] {message}{reset}"
- else:
- return f"{prefix}{color}{symbol} {message}{reset}"
-
- def debug(self, message: str, indent: int = 0) -> None:
- """Log debug message."""
- print(self._format_message(LogLevel.DEBUG, message, indent))
-
- def info(self, message: str, indent: int = 0) -> None:
- """Log info message."""
- print(self._format_message(LogLevel.INFO, message, indent))
-
- def success(self, message: str, indent: int = 0) -> None:
- """Log success message."""
- print(self._format_message(LogLevel.SUCCESS, message, indent))
-
- def warning(self, message: str, indent: int = 0) -> None:
- """Log warning message."""
- print(self._format_message(LogLevel.WARNING, message, indent))
-
- def error(self, message: str, indent: int = 0) -> None:
- """Log error message."""
- print(self._format_message(LogLevel.ERROR, message, indent), file=sys.stderr)
-
- def step(self, message: str, indent: int = 0) -> None:
- """Log a step in a process."""
- print(self._format_message(LogLevel.STEP, message, indent))
-
- def progress(self, message: str, indent: int = 0) -> None:
- """Log progress information."""
- print(self._format_message(LogLevel.PROGRESS, message, indent))
-
- def separator(self, char: str = "-", length: int = 60) -> None:
- """Print a separator line."""
- print(char * length)
-
- def header(self, title: str, width: int = 60) -> None:
- """Print a formatted header."""
- if self.use_colors:
- print(f"\n\033[1m{'=' * width}\033[0m") # Bold
- print(f"\033[1m{title.center(width)}\033[0m")
- print(f"\033[1m{'=' * width}\033[0m\n")
- else:
- print(f"\n{'=' * width}")
- print(title.center(width))
- print(f"{'=' * width}\n")
-
- def box(self, title: str, width: int = 60) -> None:
- """Print a title in a box."""
- border = "═" * (width - 2)
- if self.use_colors:
- print(f"\033[1m╔{border}╗\033[0m")
- print(f"\033[1m║{title.center(width - 2)}║\033[0m")
- print(f"\033[1m╚{border}╝\033[0m")
- else:
- print(f"╔{border}╗")
- print(f"║{title.center(width - 2)}║")
- print(f"╚{border}╝")
-
- def list_item(self, item: str, indent: int = 2) -> None:
- """Print a list item."""
- prefix = " " * indent
- print(f"{prefix}• {item}")
-
- def key_value(self, key: str, value: str, indent: int = 2) -> None:
- """Print a key-value pair."""
- prefix = " " * indent
- if self.use_colors:
- print(f"{prefix}\033[1m{key}:\033[0m {value}")
- else:
- print(f"{prefix}{key}: {value}")
-
- def spinner_start(self, message: str) -> None:
- """Start a spinner (simple implementation)."""
- sys.stdout.write(f"\r{message}... ")
- sys.stdout.flush()
-
- def spinner_stop(self, success: bool = True, message: str | None = None) -> None:
- """Stop the spinner and show result."""
- if success:
- symbol = "✅" if message else "Done"
- sys.stdout.write(f"\r{symbol} {message or ''}\n")
- else:
- symbol = "❌" if message else "Failed"
- sys.stdout.write(f"\r{symbol} {message or ''}\n")
- sys.stdout.flush()
-
-
- class ProgressLogger:
- """Logger for tracking progress through multiple steps."""
-
- def __init__(self, total_steps: int, logger: Logger | None = None):
- """Initialize progress logger.
-
- Args:
- total_steps: Total number of steps
- logger: Logger instance to use (creates new if None)
- """
- self.total_steps = total_steps
- self.current_step = 0
- self.logger = logger or Logger()
- self.start_time = time.time()
-
- def next_step(self, description: str) -> None:
- """Move to next step and log it."""
- self.current_step += 1
- elapsed = time.time() - self.start_time
-
- if self.logger.use_colors:
- progress_bar = self._create_progress_bar()
- print(
- f"\n\033[1m[Step {self.current_step}/{self.total_steps}]\033[0m {progress_bar}"
- )
- self.logger.step(f"{description} (Elapsed: {elapsed:.1f}s)")
- else:
- print(f"\n[Step {self.current_step}/{self.total_steps}]")
- self.logger.step(f"{description} (Elapsed: {elapsed:.1f}s)")
-
- def _create_progress_bar(self, width: int = 20) -> str:
- """Create a simple progress bar."""
- filled = int(width * self.current_step / self.total_steps)
- bar = "█" * filled + "░" * (width - filled)
- percentage = int(100 * self.current_step / self.total_steps)
- return f"[{bar}] {percentage}%"
-
- def complete(self) -> None:
- """Mark progress as complete."""
- elapsed = time.time() - self.start_time
- self.logger.success(f"All steps completed! Total time: {elapsed:.1f}s")
-
-
- # Create default logger instance
- logger = Logger()
-
-
- # Convenience functions using default logger
- def debug(message: str, indent: int = 0) -> None:
- """Log debug message using default logger."""
- logger.debug(message, indent)
-
-
- def info(message: str, indent: int = 0) -> None:
- """Log info message using default logger."""
- logger.info(message, indent)
-
-
- def success(message: str, indent: int = 0) -> None:
- """Log success message using default logger."""
- logger.success(message, indent)
-
-
- def warning(message: str, indent: int = 0) -> None:
- """Log warning message using default logger."""
- logger.warning(message, indent)
-
-
- def error(message: str, indent: int = 0) -> None:
- """Log error message using default logger."""
- logger.error(message, indent)
-
-
- def step(message: str, indent: int = 0) -> None:
- """Log step using default logger."""
- logger.step(message, indent)
-
-
- def progress(message: str, indent: int = 0) -> None:
- """Log progress using default logger."""
- logger.progress(message, indent)
|