using Microsoft.Extensions.Logging;
namespace VFXReviewWorker;
///
/// Minimal rolling file logger (ยง7.9): one file per day under
/// %ProgramData%\VFXReviewWorker\logs, files older than 14 days pruned on
/// startup and at midnight rollover. No external logging dependencies.
///
public sealed class FileLoggerProvider : ILoggerProvider
{
private readonly object _lock = new();
private StreamWriter? _writer;
private DateOnly _currentDay;
public FileLoggerProvider()
{
Directory.CreateDirectory(WorkerOptions.LogsDir);
Prune();
}
public ILogger CreateLogger(string categoryName) => new FileLogger(this, categoryName);
internal void Write(string category, LogLevel level, string message, Exception? ex)
{
lock (_lock)
{
var today = DateOnly.FromDateTime(DateTime.Now);
if (_writer is null || today != _currentDay)
{
_writer?.Dispose();
_currentDay = today;
_writer = new StreamWriter(
Path.Combine(WorkerOptions.LogsDir, $"worker_{today:yyyyMMdd}.log"),
append: true) { AutoFlush = true };
Prune();
}
_writer.WriteLine($"{DateTime.Now:HH:mm:ss.fff} [{level,-5}] {Shorten(category)}: {message}{(ex is null ? "" : $"\n{ex}")}");
}
}
private static string Shorten(string category) => category[(category.LastIndexOf('.') + 1)..];
private static void Prune()
{
try
{
var cutoff = DateTime.Now.AddDays(-14);
foreach (var f in Directory.EnumerateFiles(WorkerOptions.LogsDir, "*.log"))
{
if (File.GetLastWriteTime(f) < cutoff) File.Delete(f);
}
}
catch { /* best effort */ }
}
public void Dispose()
{
lock (_lock) { _writer?.Dispose(); _writer = null; }
}
private sealed class FileLogger : ILogger
{
private readonly FileLoggerProvider _provider;
private readonly string _category;
public FileLogger(FileLoggerProvider provider, string category)
{
_provider = provider;
_category = category;
}
public IDisposable? BeginScope(TState state) where TState : notnull => null;
public bool IsEnabled(LogLevel logLevel) => logLevel >= LogLevel.Information;
public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter)
{
if (!IsEnabled(logLevel)) return;
_provider.Write(_category, logLevel, formatter(state, exception), exception);
}
}
}