| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- # -*- coding: utf-8 -*-
- """
- Debug-Ausgabefunktionen für Trixy.
- Stellt farbcodierte Ausgabefunktionen bereit, die zwischen
- Debug-Modus (stdout) und Logging umschalten können.
- """
- import sys
- import logging
- from datetime import datetime
- from typing import Any
- # ANSI-Farbcodes für Terminal-Ausgabe
- class Colors:
- """ANSI-Escape-Codes für farbige Terminal-Ausgabe."""
- RESET = "\033[0m"
- BOLD = "\033[1m"
- # Vordergrundfarben
- BLACK = "\033[30m"
- RED = "\033[31m"
- GREEN = "\033[32m"
- YELLOW = "\033[33m"
- BLUE = "\033[34m"
- MAGENTA = "\033[35m"
- CYAN = "\033[36m"
- WHITE = "\033[37m"
- # Helle Varianten
- BRIGHT_RED = "\033[91m"
- BRIGHT_GREEN = "\033[92m"
- BRIGHT_YELLOW = "\033[93m"
- BRIGHT_BLUE = "\033[94m"
- BRIGHT_MAGENTA = "\033[95m"
- BRIGHT_CYAN = "\033[96m"
- BRIGHT_WHITE = "\033[97m"
- # Globaler Debug-Modus-Flag
- _debug_mode: bool = False
- # Logger für nicht-Debug-Modus
- _logger = logging.getLogger("trixy")
- # File-Router für automatisches File-Logging (optional)
- _file_router = None
- def set_debug_mode(enabled: bool) -> None:
- """
- Aktiviert oder deaktiviert den Debug-Modus.
- Im Debug-Modus werden alle Ausgaben direkt auf stdout geschrieben.
- Andernfalls wird das Logging-System verwendet.
- Args:
- enabled: True für Debug-Modus (stdout), False für Logging
- """
- global _debug_mode
- _debug_mode = enabled
- def is_debug_mode() -> bool:
- """Gibt zurück, ob der Debug-Modus aktiv ist."""
- return _debug_mode
- def _setup_file_routing(router) -> None:
- """
- Setzt den File-Router fuer automatisches File-Logging.
- Args:
- router: FileLogRouter-Instanz oder None zum Deaktivieren
- """
- global _file_router
- _file_router = router
- def _format_message(level: str, color: str, message: str, *args: Any) -> str:
- """
- Formatiert eine Debug-Nachricht mit Zeitstempel und Farbe.
- Args:
- level: Log-Level-String (INFO, DEBUG, ERROR, WARN)
- color: ANSI-Farbcode
- message: Nachrichtenformat-String
- *args: Argumente für Format-String
- Returns:
- Formatierte Nachricht mit Farbe und Zeitstempel
- """
- timestamp = datetime.now().strftime("%H:%M:%S")
- if args:
- try:
- formatted_msg = message % args
- except TypeError:
- formatted_msg = message + " " + " ".join(str(a) for a in args)
- else:
- formatted_msg = message
- return f"{Colors.BRIGHT_WHITE}[{timestamp}]{Colors.RESET} {color}{Colors.BOLD}[{level}]{Colors.RESET} {formatted_msg}"
- def _output(level: str, color: str, log_level: int, message: str, *args: Any) -> None:
- """
- Gibt eine Nachricht aus (Debug-Modus) oder loggt sie.
- Args:
- level: Level-String für Anzeige
- color: ANSI-Farbcode
- log_level: logging-Level für Logger
- message: Nachricht oder Format-String
- *args: Argumente für Format-String
- """
- if _debug_mode:
- formatted = _format_message(level, color, message, *args)
- print(formatted, file=sys.stdout if log_level < logging.ERROR else sys.stderr)
- else:
- if args:
- try:
- _logger.log(log_level, message, *args)
- except TypeError:
- _logger.log(log_level, message + " " + " ".join(str(a) for a in args))
- else:
- _logger.log(log_level, message)
- # File-Logging (in beiden Modi, gesteuert durch file_log_level)
- if _file_router is not None:
- if args:
- try:
- file_msg = message % args
- except TypeError:
- file_msg = message + " " + " ".join(str(a) for a in args)
- else:
- file_msg = message
- _file_router.log(log_level, file_msg)
- def pinfo(message: str, *args: Any) -> None:
- """
- Gibt eine Info-Nachricht aus.
- Args:
- message: Nachricht oder Format-String
- *args: Argumente für Format-String
- """
- _output("INFO", Colors.BRIGHT_GREEN, logging.INFO, message, *args)
- def pdebug(message: str, *args: Any) -> None:
- """
- Gibt eine Debug-Nachricht aus.
- Args:
- message: Nachricht oder Format-String
- *args: Argumente für Format-String
- """
- _output("DEBUG", Colors.BRIGHT_CYAN, logging.DEBUG, message, *args)
- def perror(message: str, *args: Any) -> None:
- """
- Gibt eine Fehlermeldung aus.
- Args:
- message: Nachricht oder Format-String
- *args: Argumente für Format-String
- """
- _output("ERROR", Colors.BRIGHT_RED, logging.ERROR, message, *args)
- def pwarn(message: str, *args: Any) -> None:
- """
- Gibt eine Warnmeldung aus.
- Args:
- message: Nachricht oder Format-String
- *args: Argumente für Format-String
- """
- _output("WARN", Colors.BRIGHT_YELLOW, logging.WARNING, message, *args)
- def pprogress(label: str, current: int, total: int, suffix: str = "") -> None:
- """
- Zeigt eine sich in-place aktualisierende Progressbar an.
- Im Debug-Modus: Terminal-Progressbar mit \\r
- Sonst: Nur alle 5% eine Log-Zeile
- Args:
- label: Beschriftung der Progressbar
- current: Aktueller Fortschritt in Bytes
- total: Gesamtgroesse in Bytes
- suffix: Zusaetzliche Info (Geschwindigkeit, ETA)
- """
- if total <= 0:
- return
- percent = (current / total) * 100
- if _debug_mode:
- # Terminal-Breite ermitteln
- try:
- import shutil
- term_width = shutil.get_terminal_size().columns
- except Exception:
- term_width = 80
- # Label auf max. 25 Zeichen kuerzen
- short_label = label if len(label) <= 25 else "..." + label[-22:]
- # Progressbar-Laenge dynamisch (Minimum 15)
- # Aufbau: [HH:MM:SS] [PROGRESS] label: [bar] xx.x% suffix
- # ANSI-Codes zaehlen nicht zur sichtbaren Breite
- fixed_len = 10 + 1 + 10 + 1 + len(short_label) + 2 + 2 + 7 + 1 + len(suffix)
- bar_length = max(15, term_width - fixed_len)
- if bar_length > 30:
- bar_length = 30
- filled = int(bar_length * current / total)
- bar = "█" * filled + "░" * (bar_length - filled)
- timestamp = datetime.now().strftime("%H:%M:%S")
- line = (
- f"\r{Colors.BRIGHT_WHITE}[{timestamp}]{Colors.RESET} "
- f"{Colors.BRIGHT_BLUE}{Colors.BOLD}[PROGRESS]{Colors.RESET} "
- f"{short_label}: [{bar}] {percent:5.1f}% {suffix}"
- )
- # Zeile mit Leerzeichen auf Terminal-Breite auffuellen (alte Zeichen loeschen)
- visible_len = 10 + 1 + 10 + 1 + len(short_label) + 2 + bar_length + 2 + 7 + len(suffix)
- padding = max(0, term_width - visible_len)
- line += " " * padding
- print(line, end="", flush=True)
- # Bei 100%: Newline ausgeben
- if current >= total:
- print()
- else:
- # Logging-Modus: Nur alle 5%
- current_step = int(percent / 5)
- prev_percent = ((current - 8192) / total) * 100 if current > 8192 else -1
- prev_step = int(prev_percent / 5)
- if current >= total or current_step > prev_step:
- _logger.info(f"{label}: {percent:.0f}% {suffix}")
|