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