Verläufer Diagnose

Report
This commit is contained in:
2026-07-01 15:41:29 +02:00
parent ed7946850d
commit b331301fd8
9 changed files with 245 additions and 14 deletions

View File

@@ -0,0 +1,160 @@
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";
}
}

View File

@@ -88,7 +88,6 @@ namespace BS.Shared.Core
message.ReplyToList.Add(new MailAddress(fromAddress, fromName));
}
message.BodyEncoding = Encoding.UTF8;
message.IsBodyHtml = false;

View File

@@ -119,6 +119,9 @@
</Reference>
<Reference Include="System.Drawing" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Runtime.InteropServices.RuntimeInformation, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.Serialization">
<RequiredTargetFramework>3.0</RequiredTargetFramework>
</Reference>
@@ -146,6 +149,7 @@
<Compile Include="Core\BeWoFault.cs" />
<Compile Include="Core\ComplexWohnheimbuchungsRelationHelper.cs" />
<Compile Include="Core\AbstractIDSpecificDefaultClass.cs" />
<Compile Include="Core\DiagnosticContext.cs" />
<Compile Include="Core\Facade\HttpClientFacade.cs" />
<Compile Include="Core\Facade\JsonHttpClientFacade.cs" />
<Compile Include="Core\JsonUtils.cs" />

View File

@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net452" />
<package id="System.Runtime.InteropServices.RuntimeInformation" version="4.3.0" targetFramework="net452" />
</packages>