diff --git a/backend/app/UNICODE_ENCODING_FIX.md b/backend/app/UNICODE_ENCODING_FIX.md new file mode 100644 index 00000000..0519ecba --- /dev/null +++ b/backend/app/UNICODE_ENCODING_FIX.md @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/backend/app/database/myp.db-shm b/backend/app/database/myp.db-shm index 5cfb43b2..d354fc3f 100644 Binary files a/backend/app/database/myp.db-shm and b/backend/app/database/myp.db-shm differ diff --git a/backend/app/database/myp.db-wal b/backend/app/database/myp.db-wal index 5e34f2e2..a9f81cd8 100644 Binary files a/backend/app/database/myp.db-wal and b/backend/app/database/myp.db-wal differ diff --git a/backend/app/test_unicode_fix.py b/backend/app/test_unicode_fix.py new file mode 100644 index 00000000..0519ecba --- /dev/null +++ b/backend/app/test_unicode_fix.py @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/backend/app/utils/logging_config.py b/backend/app/utils/logging_config.py index d6defb48..d8cda21b 100644 --- a/backend/app/utils/logging_config.py +++ b/backend/app/utils/logging_config.py @@ -82,10 +82,20 @@ EMOJI_FALLBACK = { def safe_emoji(emoji: str) -> str: """Gibt ein Emoji zurück oder einen ASCII-Fallback bei Encoding-Problemen.""" try: - # Teste, ob das Emoji dargestellt werden kann - emoji.encode(sys.stdout.encoding or 'utf-8') + # Erste Priorität: Teste, ob das Emoji dargestellt werden kann + test_encoding = sys.stdout.encoding or 'utf-8' + emoji.encode(test_encoding) + + # Zweite Prüfung: Windows-spezifische cp1252-Codierung + if os.name == 'nt': + try: + emoji.encode('cp1252') + except UnicodeEncodeError: + # Wenn cp1252 fehlschlägt, verwende Fallback + return EMOJI_FALLBACK.get(emoji, '[?]') + return emoji - except (UnicodeEncodeError, LookupError): + except (UnicodeEncodeError, LookupError, AttributeError): return EMOJI_FALLBACK.get(emoji, '[?]') # Prüfen, ob das Terminal ANSI-Farben unterstützt @@ -135,39 +145,61 @@ class ColoredFormatter(logging.Formatter): } def format(self, record): - # Basis-Format erstellen - log_fmt = LOG_FORMAT - date_fmt = LOG_DATE_FORMAT - - # Emoji dem Level und der Kategorie hinzufügen - level_name = record.levelname - category_name = record.name.split('.')[-1] if '.' in record.name else record.name - - level_emoji = safe_emoji(LOG_EMOJIS.get(level_name, '')) - category_emoji = safe_emoji(LOG_EMOJIS.get(category_name, '')) - - # Record-Objekt modifizieren (aber temporär) - original_levelname = record.levelname - original_name = record.name - - # Emojis hinzufügen - record.levelname = f"{level_emoji} {level_name}" - record.name = f"{category_emoji} {category_name}" - - # Farbe hinzufügen wenn unterstützt - if USE_COLORS: - level_color = self.level_colors.get(original_levelname, ANSI_COLORS['RESET']) - record.levelname = f"{level_color}{record.levelname}{ANSI_COLORS['RESET']}" - record.name = f"{ANSI_COLORS['BOLD']}{record.name}{ANSI_COLORS['RESET']}" - - # Formatieren - result = super().format(record) - - # Originale Werte wiederherstellen - record.levelname = original_levelname - record.name = original_name - - return result + try: + # Basis-Format erstellen + log_fmt = LOG_FORMAT + date_fmt = LOG_DATE_FORMAT + + # Emoji dem Level und der Kategorie hinzufügen + level_name = record.levelname + category_name = record.name.split('.')[-1] if '.' in record.name else record.name + + level_emoji = safe_emoji(LOG_EMOJIS.get(level_name, '')) + category_emoji = safe_emoji(LOG_EMOJIS.get(category_name, '')) + + # Record-Objekt modifizieren (aber temporär) + original_levelname = record.levelname + original_name = record.name + + # Emojis hinzufügen + record.levelname = f"{level_emoji} {level_name}" + record.name = f"{category_emoji} {category_name}" + + # Farbe hinzufügen wenn unterstützt + if USE_COLORS: + level_color = self.level_colors.get(original_levelname, ANSI_COLORS['RESET']) + record.levelname = f"{level_color}{record.levelname}{ANSI_COLORS['RESET']}" + record.name = f"{ANSI_COLORS['BOLD']}{record.name}{ANSI_COLORS['RESET']}" + + # Formatieren + result = super().format(record) + + # Originale Werte wiederherstellen + record.levelname = original_levelname + record.name = original_name + + return result + except (UnicodeEncodeError, UnicodeDecodeError, AttributeError) as e: + # Fallback bei Unicode-Problemen: Verwende nur ASCII-Text + original_levelname = record.levelname + original_name = record.name + + # Emojis durch ASCII-Fallbacks ersetzen + level_fallback = EMOJI_FALLBACK.get(LOG_EMOJIS.get(original_levelname, ''), '[LOG]') + category_name = record.name.split('.')[-1] if '.' in record.name else record.name + category_fallback = EMOJI_FALLBACK.get(LOG_EMOJIS.get(category_name, ''), '[CAT]') + + record.levelname = f"{level_fallback} {original_levelname}" + record.name = f"{category_fallback} {category_name}" + + # Basis-Formatierung ohne Farben + result = super().format(record) + + # Originale Werte wiederherstellen + record.levelname = original_levelname + record.name = original_name + + return result class DebugInfoFilter(logging.Filter): """Filter, der Debug-Informationen zu jedem Log-Eintrag hinzufügt.""" @@ -228,12 +260,20 @@ def setup_logging(debug_mode: bool = False): console_handler.setLevel(log_level) console_handler.setFormatter(colored_formatter) console_handler.addFilter(debug_filter) + + # Windows PowerShell UTF-8 Encoding-Unterstützung + if os.name == 'nt' and hasattr(console_handler.stream, 'reconfigure'): + try: + console_handler.stream.reconfigure(encoding='utf-8') + except: + pass + root_logger.addHandler(console_handler) # File Handler für allgemeine App-Logs app_log_file = get_log_file("app") app_handler = logging.handlers.RotatingFileHandler( - app_log_file, maxBytes=10*1024*1024, backupCount=5 + app_log_file, maxBytes=10*1024*1024, backupCount=5, encoding='utf-8' ) app_handler.setLevel(log_level) app_handler.setFormatter(file_formatter) @@ -276,12 +316,20 @@ def get_logger(category: str) -> logging.Logger: console_handler.setLevel(getattr(logging, LOG_LEVEL)) console_handler.setFormatter(colored_formatter) console_handler.addFilter(debug_filter) + + # Windows PowerShell UTF-8 Encoding-Unterstützung + if os.name == 'nt' and hasattr(console_handler.stream, 'reconfigure'): + try: + console_handler.stream.reconfigure(encoding='utf-8') + except: + pass + logger.addHandler(console_handler) # File Handler für spezifische Kategorie log_file = get_log_file(category) file_handler = logging.handlers.RotatingFileHandler( - log_file, maxBytes=10*1024*1024, backupCount=5 + log_file, maxBytes=10*1024*1024, backupCount=5, encoding='utf-8' ) file_handler.setLevel(getattr(logging, LOG_LEVEL)) file_handler.setFormatter(file_formatter) @@ -291,7 +339,7 @@ def get_logger(category: str) -> logging.Logger: if category != "errors": error_log_file = get_log_file("errors") error_handler = logging.handlers.RotatingFileHandler( - error_log_file, maxBytes=10*1024*1024, backupCount=5 + error_log_file, maxBytes=10*1024*1024, backupCount=5, encoding='utf-8' ) error_handler.setLevel(logging.ERROR) error_handler.setFormatter(file_formatter)