Zentrale Exception-Behandlung (Dispatcher.UIThread.UnhandledException, AppDomain, TaskScheduler) verhindert Abstürze und protokolliert Fehler über AppLogger in eine rotierende Log-Datei im App-Datenverzeichnis. Toast-Benachrichtigungen zeigen Erfolg/Fehler global an. Alle Dialoge mit Formularfeldern zeigen Validierungsmeldungen jetzt direkt am betroffenen Feld statt in einem Sammel-Label. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
78 lines
2.5 KiB
C#
78 lines
2.5 KiB
C#
namespace LehrerApp.Core.Services;
|
|
|
|
public enum LogLevel { Info, Warning, Error }
|
|
|
|
/// <summary>
|
|
/// Schreibt Log-Zeilen in eine tägliche Datei im App-Datenverzeichnis
|
|
/// (<c><AppData>/LehrerApp/logs/lehrerapp-JJJJ-MM-TT.log</c>).
|
|
/// Beim Start werden Dateien entfernt, die älter als <see cref="RetentionDays"/> sind.
|
|
/// </summary>
|
|
public class AppLogger
|
|
{
|
|
private const int RetentionDays = 14;
|
|
private readonly object _lock = new();
|
|
private readonly string _logDirectory;
|
|
|
|
public string LogDirectory => _logDirectory;
|
|
|
|
public AppLogger(string appDataPath)
|
|
{
|
|
_logDirectory = Path.Combine(appDataPath, "logs");
|
|
Directory.CreateDirectory(_logDirectory);
|
|
RotateOldFiles();
|
|
}
|
|
|
|
public void Info(string message) => Write(LogLevel.Info, message, null);
|
|
public void Warn(string message) => Write(LogLevel.Warning, message, null);
|
|
public void Error(string message, Exception? exception = null) => Write(LogLevel.Error, message, exception);
|
|
|
|
private void Write(LogLevel level, string message, Exception? exception)
|
|
{
|
|
var levelLabel = level switch
|
|
{
|
|
LogLevel.Info => "INFO ",
|
|
LogLevel.Warning => "WARN ",
|
|
LogLevel.Error => "ERROR",
|
|
_ => "? ",
|
|
};
|
|
var line = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} [{levelLabel}] {message}";
|
|
if (exception is not null)
|
|
line += Environment.NewLine + exception;
|
|
|
|
lock (_lock)
|
|
{
|
|
try
|
|
{
|
|
File.AppendAllText(CurrentLogFile(), line + Environment.NewLine);
|
|
}
|
|
catch
|
|
{
|
|
// Logging darf die App nie zum Absturz bringen (z.B. Datenträger voll/gesperrt).
|
|
}
|
|
}
|
|
}
|
|
|
|
private string CurrentLogFile() =>
|
|
Path.Combine(_logDirectory, $"lehrerapp-{DateTime.Now:yyyy-MM-dd}.log");
|
|
|
|
private void RotateOldFiles()
|
|
{
|
|
var cutoff = DateTime.Now.AddDays(-RetentionDays);
|
|
try
|
|
{
|
|
foreach (var file in Directory.GetFiles(_logDirectory, "lehrerapp-*.log"))
|
|
{
|
|
if (File.GetLastWriteTime(file) < cutoff)
|
|
{
|
|
try { File.Delete(file); }
|
|
catch { /* nächster Start versucht es erneut */ }
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// Verzeichnis nicht lesbar o.ä. — Logging soll trotzdem weiterlaufen können.
|
|
}
|
|
}
|
|
}
|