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
parent
3bb82ff968
commit
2ce78b6c65
|
|
@ -0,0 +1,7 @@
|
|||
namespace UtopiaCanteenSystem.Services.Logging;
|
||||
|
||||
public enum FileLogTarget
|
||||
{
|
||||
Backend,
|
||||
Client
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue