using System; using System.IO; using System.Linq; namespace Project_Periodensystem.Model { /// /// Zentrale Logging-Klasse für Debug-Ausgaben und Fehlerprotokollierung /// /// ZWECK UND ARCHITEKTUR: /// - Einheitliche Logging-Infrastruktur für die gesamte Anwendung /// - Dual-Output: Konsole (Development) und Datei (Production/Debugging) /// - Verschiedene Log-Level für strukturierte Protokollierung /// - Demonstration verschiedener C#-Konzepte und Datentypen /// /// DESIGN PATTERNS: /// - Static Class: Globaler Zugriff ohne Instanziierung /// - Facade Pattern: Vereinfachte API für komplexe Logging-Operationen /// - Strategy Pattern: Verschiedene Ausgabekanäle (Konsole, Datei) /// /// C# KONZEPTE: /// - Static Constructor: Einmalige Initialisierung beim ersten Zugriff /// - Exception Handling: Try-Catch für robuste Datei-Operationen /// - Method Overloading: Verschiedene LogArray-Varianten /// - Conditional Compilation: [Conditional("DEBUG")] für LogDebug /// - LINQ: Select() für Datenformatierung /// - String Interpolation: $"Text {variable}" für lesbare Ausgaben /// /// SICHERHEIT UND ROBUSTHEIT: /// - Alle Datei-Operationen sind exception-safe /// - Logging-Fehler bringen die Hauptanwendung nicht zum Absturz /// - Graceful Degradation bei fehlenden Dateiberechtigungen /// public static class Logger { /// /// Pfad zur Log-Datei im lokalen Anwendungsverzeichnis /// /// PFAD-KONSTRUKTION: /// - Environment.SpecialFolder.LocalApplicationData: Benutzer-spezifischer App-Ordner /// - Windows: C:\Users\[Username]\AppData\Local\Periodensystem\app.log /// - Cross-Platform: Funktioniert auf Windows, Linux, macOS /// /// C# KONZEPTE: /// - Path.Combine(): Sichere Pfad-Konstruktion (OS-unabhängig) /// - Environment.SpecialFolder: Standard-Verzeichnisse des Betriebssystems /// - Readonly Field: Kann nur bei Deklaration oder im static Constructor gesetzt werden /// private static readonly string LogFilePath = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Periodensystem", "app.log"); /// /// Statischer Konstruktor - Einmalige Initialisierung der Logger-Infrastruktur /// /// ZWECK: /// - Erstellt das Log-Verzeichnis falls es nicht existiert /// - Wird automatisch beim ersten Zugriff auf die Klasse aufgerufen /// - Kann nicht explizit aufgerufen werden /// /// C# KONZEPTE: /// - Static Constructor: Automatische Initialisierung ohne Parameter /// - Path.GetDirectoryName(): Extrahiert Verzeichnis aus vollständigem Pfad /// - Directory.CreateDirectory(): Erstellt Verzeichnis-Hierarchie rekursiv /// - Exception Handling: Fehler werden "geschluckt" für Robustheit /// /// FEHLERBEHANDLUNG: /// - Bei Problemen wird nur Konsolen-Logging verwendet /// - Keine Exception nach außen, da Logging optional ist /// static Logger() { try { var logDirectory = Path.GetDirectoryName(LogFilePath); if (logDirectory != null && !Directory.Exists(logDirectory)) { Directory.CreateDirectory(logDirectory); } } catch { // Falls Log-Datei nicht erstellt werden kann, nur Konsole verwenden // Graceful Degradation: Hauptfunktionalität nicht beeinträchtigen } } // ===== HAUPTPROTOKOLLIERUNGS-METHODEN ===== /// /// Zentrale Logging-Methode - protokolliert Nachrichten dual (Konsole + Datei) /// /// ZWECK: /// - Universelle Protokollierung für alle Anwendungsereignisse /// - Entwickler sehen Ausgaben in Konsole (sofortiges Feedback) /// - Benutzer/Support kann Logs aus Datei analysieren /// /// C# KONZEPTE: /// - String.IsNullOrWhiteSpace(): Robuste Null/Empty/Whitespace-Prüfung /// - DateTime.Now.ToString(): Formatierte Zeitstempel /// - String Interpolation: $"[{timestamp}] {message}" /// - File.AppendAllText(): Atomare Datei-Operation /// - Environment.NewLine: OS-spezifische Zeilentrenner /// /// FEHLERBEHANDLUNG: /// - Guard Clause: Leere Nachrichten werden ignoriert /// - Try-Catch um Datei-Operation: Konsole funktioniert immer /// - Keine Exception nach außen: Logging darf nie Hauptanwendung stören /// /// THREAD-SAFETY: /// - File.AppendAllText ist thread-safe /// - Console.WriteLine ist thread-safe /// - Methode kann von mehreren Threads gleichzeitig aufgerufen werden /// /// Zu protokollierende Nachricht (wird validiert) public static void Log(string message) { // Guard Clause: Ungültige Eingaben frühzeitig abfangen if (string.IsNullOrWhiteSpace(message)) return; // Zeitstempel im ISO-ähnlichen Format für bessere Lesbarkeit var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); var logEntry = $"[{timestamp}] {message}"; // Konsolen-Ausgabe (für Entwicklung und sofortiges Feedback) Console.WriteLine(logEntry); // Datei-Ausgabe (für Persistenz und spätere Analyse) try { File.AppendAllText(LogFilePath, logEntry + Environment.NewLine); } catch { // Fehler beim Schreiben ignorieren um App nicht zum Absturz zu bringen // Silent Fail: Logging ist nice-to-have, nicht essential } } // ===== SPEZIALISIERTE LOGGING-METHODEN ===== /// /// Protokolliert Exceptions mit vollständigen Diagnose-Informationen /// /// ZWECK: /// - Strukturierte Fehlerprotokollierung für Debugging /// - Vollständige Stack-Trace-Information für Fehleranalyse /// - Kontextuelle Information für bessere Problemlösung /// /// C# KONZEPTE: /// - Exception Handling: ex.Message und ex.StackTrace Properties /// - Null-Check: Schutz vor null-Exceptions /// - String Interpolation: Eingebettete Variablen in Strings /// - Conditional Operator: condition ? valueIfTrue : valueIfFalse /// - Multi-Line Strings: \n für Zeilenumbrüche /// /// PARAMETER: /// - ex: Die zu protokollierende Exception (kann theoretisch null sein) /// - context: Optional - zusätzlicher Kontext (Methodenname, Operation, etc.) /// /// VERWENDUNG: /// try { riskyOperation(); } catch (Exception ex) { Logger.LogException(ex, "DataLoad"); } /// /// Exception die protokolliert werden soll /// Zusätzlicher Kontext (Methodenname, Operation, etc.) public static void LogException(Exception ex, string context = "") { // Null-Schutz: Auch ungültige Exception-Objekte handhaben if (ex == null) { Log("Null-Exception übergeben - möglicher Programmierfehler"); return; } // Formatierung je nach verfügbarem Kontext var message = string.IsNullOrWhiteSpace(context) ? $"EXCEPTION: {ex.Message}\nStack: {ex.StackTrace}" : $"EXCEPTION in {context}: {ex.Message}\nStack: {ex.StackTrace}"; Log(message); } /// /// Protokolliert Warnungen - für nicht-kritische Probleme /// /// ZWECK: /// - Kennzeichnung von Problemen die behoben werden sollten /// - Unterscheidung von Informationen und echten Fehlern /// - Monitoring von potentiellen Problemen /// /// BEISPIELE: /// - Veraltete API-Aufrufe /// - Performance-Probleme /// - Unerwartete aber handhabbare Situationen /// /// Warnung (wird mit WARNING: Präfix versehen) public static void LogWarning(string message) { Log($"WARNING: {message}"); } /// /// Protokolliert Fehler - für schwerwiegende Probleme /// /// ZWECK: /// - Kennzeichnung von Fehlern die Funktionalität beeinträchtigen /// - Höhere Priorität als Warnings bei Log-Analyse /// - Monitoring von kritischen Problemen /// /// BEISPIELE: /// - Datenverlust oder Corruption /// - Netzwerk-/Datenbankfehler /// - Konfigurationsprobleme /// /// Fehlermeldung (wird mit ERROR: Präfix versehen) public static void LogError(string message) { Log($"ERROR: {message}"); } /// /// Protokolliert Debug-Informationen - nur in Debug-Builds kompiliert /// /// ZWECK: /// - Detaillierte Entwicklungs-Informationen /// - Automatische Entfernung in Release-Builds /// - Performance-Optimierung (keine Debug-Logs in Production) /// /// C# KONZEPTE: /// - Conditional Compilation: [Conditional("DEBUG")] /// - Attribute-based Programming: Verhalten durch Metadaten steuern /// - Build Configuration: Unterschiedliches Verhalten je nach Build-Typ /// /// COMPILER-VERHALTEN: /// - Debug Build: Methode wird normal ausgeführt /// - Release Build: Methoden-Aufrufe werden vollständig entfernt /// - Zero Performance Impact in Production /// /// Debug-Nachricht (wird mit DEBUG: Präfix versehen) [System.Diagnostics.Conditional("DEBUG")] public static void LogDebug(string message) { Log($"DEBUG: {message}"); } // ===== DATEI-MANAGEMENT UND UTILITY-METHODEN ===== /// /// Löscht die Log-Datei für Cleanup-Operationen /// /// ZWECK: /// - Speicherplatz freigeben bei großen Log-Dateien /// - Fresh Start für neue Test-Sessions /// - Maintenance-Operation für Anwendungs-Lifecycle /// /// C# KONZEPTE: /// - File.Exists(): Existenz-Prüfung vor Operation /// - File.Delete(): Atomare Datei-Löschung /// - Exception Handling: Robust gegen Berechtigungsprobleme /// /// SICHERHEIT: /// - Prüft Existenz vor Löschung (vermeidet FileNotFoundException) /// - Catch für Berechtigungsfehler oder Datei-in-Verwendung /// - Loggt eigene Operationen (self-documenting) /// public static void ClearLog() { try { if (File.Exists(LogFilePath)) { File.Delete(LogFilePath); Log("Log-Datei gelöscht und neu initialisiert"); } } catch (Exception ex) { Log($"Fehler beim Löschen der Log-Datei: {ex.Message}"); } } /// /// Gibt den vollständigen Pfad zur Log-Datei zurück /// /// ZWECK: /// - Ermöglicht externen Tools Zugriff auf Log-Datei /// - Debugging: Entwickler kann Log-Datei manuell öffnen /// - Integration: Andere Komponenten können Logs lesen /// /// KAPSELUNG: /// - Kontrollierter Zugriff auf private LogFilePath /// - Readonly-Zugriff: Externe Komponenten können Pfad nicht ändern /// - Information Hiding: Implementation Details bleiben verborgen /// /// Vollständiger Pfad zur Log-Datei (absoluter Pfad) public static string GetLogFilePath() { return LogFilePath; } // ===== SPEZIALISIERTE DATENTYP-LOGGING (C# LERNZWECK) ===== /// /// Loggt String-Arrays - demonstriert Array-Verarbeitung /// /// ZWECK: /// - Zeigt Array-Inhalte in lesbarer Form /// - Demonstriert string.Join() für Array-zu-String-Konvertierung /// - Nützlich für Listen von Elementsymbolen, Namen, etc. /// /// C# KONZEPTE: /// - Method Overloading: Mehrere LogArray-Methoden für verschiedene Typen /// - string.Join(): Elegante Array-zu-String-Konvertierung /// - Array-Parameter: string[] als Eingabe /// /// BEISPIEL-AUSGABE: /// "Element-Symbole: [H, He, Li, Be, B, C, N, O, F, Ne]" /// /// Beschreibender Titel für das Array /// Array von Strings zur Anzeige public static void LogArray(string title, string[] items) { Log($"{title}: [{string.Join(", ", items)}]"); } /// /// Loggt Double-Arrays - demonstriert numerische Array-Verarbeitung /// /// ZWECK: /// - Zeigt numerische Daten in formatierter Form /// - Demonstriert LINQ Select() für Datenformatierung /// - Nützlich für Atomgewichte, Dichten, Elektronegativitäten /// /// C# KONZEPTE: /// - LINQ Select(): Funktionale Programmierung für Datentransformation /// - Lambda Expression: n => n.ToString("F2") für Formatierung /// - ToString() mit Format-String: "F2" für 2 Dezimalstellen /// - Method Overloading: Verschiedene Typen, gleicher Methodenname /// /// BEISPIEL-AUSGABE: /// "Atomgewichte: [1.01, 4.00, 6.94, 9.01, 10.81]" /// /// Beschreibender Titel für das Array /// Array von Double-Werten zur Anzeige public static void LogArray(string title, double[] numbers) { // LINQ Select() wandelt jede Zahl in formatierten String um var formattedNumbers = numbers.Select(n => n.ToString("F2")).ToArray(); Log($"{title}: [{string.Join(", ", formattedNumbers)}]"); } /// /// Loggt Tuple-Informationen - demonstriert strukturierte Datenausgabe /// /// ZWECK: /// - Zeigt Tuple-Inhalte in lesbarer Form /// - Demonstriert strukturierte Datenprotokollierung /// - Nützlich für Element-Statistiken und zusammengehörige Werte /// /// C# KONZEPTE: /// - Tuple: Strukturierte Datencontainer /// - String als Parameter: Bereits formatierte Tuple-Darstellung /// - Delegation: Ruft zentrale Log()-Methode auf /// /// VERWENDUNG: /// var stats = GetStatistics(); // returns (int, int, double) /// Logger.LogTuple("Statistiken", $"Elemente: {stats.Item1}, Serien: {stats.Item2}"); /// /// BEISPIEL-AUSGABE: /// "Element-Info: Symbol: H, Name: Hydrogen, Gewicht: 1.01" /// /// Beschreibender Titel für das Tuple /// Bereits formatierte Tuple-Darstellung als String public static void LogTuple(string title, string tupleInfo) { Log($"{title}: {tupleInfo}"); } } }