161 lines
5.4 KiB
C#
161 lines
5.4 KiB
C#
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
|
|
{
|
|
/// <summary>
|
|
/// Sammelt Systemkontext-Informationen, die einer Exception-Mail
|
|
/// beigelegt werden können, um Support/Debugging zu erleichtern.
|
|
/// </summary>
|
|
public static class DiagnosticContext
|
|
{
|
|
// Ringpuffer für die letzten User-Aktionen (Thread-safe)
|
|
private static readonly ConcurrentQueue<string> _actionLog = new ConcurrentQueue<string>();
|
|
private const int MaxActionLogEntries = 10;
|
|
|
|
private static readonly DateTime _appStartTime = DateTime.UtcNow;
|
|
|
|
/// <summary>
|
|
/// Optionale Mapping-Funktion: MachineName -> "sprechender" Servername.
|
|
/// Vom Aufrufer beim Start gesetzt, z. B. aus einer Config.
|
|
/// </summary>
|
|
public static Func<string, string> MachineNameResolver { get; set; }
|
|
= machineName => machineName; // Fallback: keine Übersetzung
|
|
|
|
/// <summary>
|
|
/// Registriert eine User-Aktion im Ringpuffer (z. B. Button-Klicks, Commands, Navigation).
|
|
/// Am besten zentral im Command-Handling oder Navigation-Service aufrufen.
|
|
/// </summary>
|
|
public static void LogAction(string action)
|
|
{
|
|
_actionLog.Enqueue($"{DateTime.UtcNow:HH:mm:ss.fff} - {action}");
|
|
while (_actionLog.Count > MaxActionLogEntries)
|
|
{
|
|
_actionLog.TryDequeue(out _);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Baut den vollständigen Diagnose-Report als String, z. B. zum Anhängen an eine Exception-Mail.
|
|
/// </summary>
|
|
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";
|
|
}
|
|
}
|