using System.IO; using System.Text; namespace UtopiaCanteenSystem.Services.Logging; /// /// Thread-safe daily file logger. Never throws to callers. /// public static class FileLogger { private static readonly object Lock = new(); private static FileLogTarget? _target; public static bool IsConfigured => _target.HasValue; public static void ConfigureBackend() => Configure(FileLogTarget.Backend); public static void ConfigureClient() => Configure(FileLogTarget.Client); public static void Configure(FileLogTarget target) { _target = target; var dir = GetLogsDirectory(); try { Directory.CreateDirectory(dir); } catch { // Ignore } Info("FileLogger", $"Logging initialized. Directory={dir}"); } public static string GetLogsDirectory() => _target == FileLogTarget.Client ? LogPaths.ClientLogsDirectory : LogPaths.BackendLogsDirectory; public static void Debug(string component, string message) => Write("DEBUG", component, message, null); public static void Info(string component, string message) => Write("INFO", component, message, null); public static void Warn(string component, string message, Exception? ex = null) => Write("WARN", component, message, ex); public static void Error(string component, string message, Exception? ex = null) => Write("ERROR", component, message, ex); private static void Write(string level, string component, string message, Exception? ex) { if (!_target.HasValue) return; try { var dir = GetLogsDirectory(); var prefix = _target == FileLogTarget.Client ? "client" : "backend"; var file = Path.Combine(dir, $"{prefix}-{DateTime.Now:yyyy-MM-dd}.log"); var line = FormatLine(level, component, message, ex); lock (Lock) { Directory.CreateDirectory(dir); File.AppendAllText(file, line, Encoding.UTF8); } } catch { // Never crash the app because logging failed. } } private static string FormatLine(string level, string component, string message, Exception? ex) { var sb = new StringBuilder(); sb.Append($"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} [{level}] [{component}] {message}"); if (ex != null) { sb.AppendLine(); sb.Append($"Exception: {ex.GetType().Name}: {ex.Message}"); if (!string.IsNullOrWhiteSpace(ex.StackTrace)) { sb.AppendLine(); sb.Append(ex.StackTrace); } if (ex.InnerException != null) { sb.AppendLine(); sb.Append($"InnerException: {ex.InnerException.GetType().Name}: {ex.InnerException.Message}"); } } sb.AppendLine(); return sb.ToString(); } }