using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Shapes; namespace BS.Shared.Core { /// /// Sammelt Systemkontext-Informationen, die einer Exception-Mail /// beigelegt werden können, um Support/Debugging zu erleichtern. /// public static class DiagnosticContext { // Ringpuffer für die letzten User-Aktionen (Thread-safe) private static readonly ConcurrentQueue _actionLog = new ConcurrentQueue(); private const int MaxActionLogEntries = 10; private static readonly DateTime _appStartTime = DateTime.UtcNow; /// /// Optionale Mapping-Funktion: MachineName -> "sprechender" Servername. /// Vom Aufrufer beim Start gesetzt, z. B. aus einer Config. /// public static Func MachineNameResolver { get; set; } = machineName => machineName; // Fallback: keine Übersetzung /// /// Registriert eine User-Aktion im Ringpuffer (z. B. Button-Klicks, Commands, Navigation). /// Am besten zentral im Command-Handling oder Navigation-Service aufrufen. /// public static void LogAction(string action) { _actionLog.Enqueue($"{DateTime.UtcNow:HH:mm:ss.fff} - {action}"); while (_actionLog.Count > MaxActionLogEntries) { _actionLog.TryDequeue(out _); } } /// /// Baut den vollständigen Diagnose-Report als String, z. B. zum Anhängen an eine Exception-Mail. /// public static string BuildReport(Exception ex = null, string currentView = null) { var sb = new StringBuilder(); sb.AppendLine("=== SYSTEM ==="); var machineName = Environment.MachineName; var friendlyName = MachineNameResolver(machineName); sb.AppendLine($"MachineName: {machineName}" + (friendlyName != machineName ? $" ({friendlyName})" : "")); sb.AppendLine($"Systemzeit (UTC): {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss}"); sb.AppendLine($"Systemzeit (lokal): {DateTime.Now:yyyy-MM-dd HH:mm:ss} ({TimeZoneInfo.Local.Id})"); sb.AppendLine($"OS: {RuntimeInformation.OSDescription}"); sb.AppendLine($"Architektur: {RuntimeInformation.OSArchitecture} / Prozess: {RuntimeInformation.ProcessArchitecture}"); sb.AppendLine($"CPU Cores: {Environment.ProcessorCount}"); sb.AppendLine($"Angemeldeter User:{Environment.UserName}"); sb.AppendLine(); sb.AppendLine("=== RESSOURCEN ==="); try { using (var proc = Process.GetCurrentProcess()) { sb.AppendLine($"Working Set: {proc.WorkingSet64 / 1024 / 1024} MB"); sb.AppendLine($"Prozess-Uptime: {DateTime.UtcNow - _appStartTime:hh\\:mm\\:ss}"); sb.AppendLine($"Prozess-ID: {proc.Id}"); } } catch (Exception e) { sb.AppendLine($"(Fehler beim Auslesen der Prozessdaten: {e.Message})"); } try { var drive = new DriveInfo(System.IO.Path.GetPathRoot(AppDomain.CurrentDomain.BaseDirectory) ?? "C:\\"); sb.AppendLine($"Freier Speicher: {drive.AvailableFreeSpace / 1024 / 1024 / 1024} GB " + $"von {drive.TotalSize / 1024 / 1024 / 1024} GB auf {drive.Name}"); } catch (Exception e) { sb.AppendLine($"(Fehler beim Auslesen des Speicherplatzes: {e.Message})"); } sb.AppendLine(); sb.AppendLine("=== ANWENDUNG ==="); var assembly = Assembly.GetEntryAssembly(); sb.AppendLine($"App-Version: {assembly?.GetName().Version}"); sb.AppendLine($".NET Runtime: {RuntimeInformation.FrameworkDescription}"); sb.AppendLine($"Environment: {AppEnvironmentInfo.CurrentEnvironmentName}"); // an deine bestehende Environment-Detection anpassen //if (!string.IsNullOrEmpty(currentView)) // sb.AppendLine($"Aktuelle View: {currentView}"); //sb.AppendLine(); //sb.AppendLine("=== NETZWERK / BACKEND ==="); //sb.AppendLine($"Ollama erreichbar: {CheckHostReachable("localhost", 500)}"); // Host/Port anpassen sb.AppendLine(); sb.AppendLine("=== LETZTE USER-AKTIONEN ==="); if (_actionLog.IsEmpty) { sb.AppendLine("(keine erfasst)"); } else { foreach (var entry in _actionLog) sb.AppendLine(entry); } if (ex != null) { sb.AppendLine(); sb.AppendLine("=== EXCEPTION ==="); AppendExceptionDetails(sb, ex); } return sb.ToString(); } private static void AppendExceptionDetails(StringBuilder sb, Exception ex, int level = 0) { var indent = new string(' ', level * 2); sb.AppendLine($"{indent}Typ: {ex.GetType().FullName}"); sb.AppendLine($"{indent}Message: {ex.Message}"); sb.AppendLine($"{indent}HResult: {ex.HResult}"); sb.AppendLine($"{indent}Source: {ex.Source}"); if (ex.Data.Count > 0) { sb.AppendLine($"{indent}Data:"); foreach (var key in ex.Data.Keys) sb.AppendLine($"{indent} {key}: {ex.Data[key]}"); } sb.AppendLine($"{indent}StackTrace:"); sb.AppendLine(ex.StackTrace); if (ex.InnerException != null) { sb.AppendLine($"{indent}--- InnerException ---"); AppendExceptionDetails(sb, ex.InnerException, level + 1); } } } // Platzhalter — an deine bestehende Environment-Detection-Logik anbinden internal static class AppEnvironmentInfo { public static string CurrentEnvironmentName { get; set; } = "Unknown"; } }