Add daily file logger target selection

Adds backend/client daily log writing with separate log prefixes and target-aware log directories.
feature/centralized-offline-canteen
SYED MUSTUFA AHMED NAQVI 2026-06-01 15:07:19 +05:00
parent 3bb82ff968
commit 2ce78b6c65
2 changed files with 106 additions and 0 deletions

View File

@ -0,0 +1,7 @@
namespace UtopiaCanteenSystem.Services.Logging;
public enum FileLogTarget
{
Backend,
Client
}

View File

@ -0,0 +1,99 @@
using System.IO;
using System.Text;
namespace UtopiaCanteenSystem.Services.Logging;
/// <summary>
/// Thread-safe daily file logger. Never throws to callers.
/// </summary>
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();
}
}