using System.Globalization; namespace Agent.App.Logging; public sealed class RollingFileLog : IDisposable { private readonly RollingFileWriter _writer; private readonly TextWriter _consoleWriter; private readonly int _minimumLevel; public RollingFileLog(string directory, long maxBytes, int retainedFiles, string minimumLevel = "information", TextWriter? consoleWriter = null) { Directory.CreateDirectory(directory); _writer = new RollingFileWriter(Path.Combine(directory, "application.log"), maxBytes, retainedFiles); _consoleWriter = consoleWriter ?? Console.Error; _minimumLevel = ParseLevel(minimumLevel); } public void Information(string message) => Write("INF", message); public void Operational(string message) => Write("INF", message, force: true); public void Warning(string message) => Write("WRN", message); public void Error(string message) => Write("ERR", message); public void Fatal(string message) => Write("FTL", message); private void Write(string level, string message, bool force = false) { if (!force && Level(level) < _minimumLevel) return; string line = string.Create(CultureInfo.InvariantCulture, $"{DateTimeOffset.UtcNow:O} [{level}] {message}"); _consoleWriter.WriteLine(line); _writer.WriteLine(line); } private static int ParseLevel(string level) => level.ToLowerInvariant() switch { "information" or "info" => 0, "warning" or "warn" => 1, "error" => 2, "fatal" => 3, _ => throw new ArgumentException($"Unknown logging level '{level}'.", nameof(level)) }; private static int Level(string level) => level switch { "INF" => 0, "WRN" => 1, "ERR" => 2, "FTL" => 3, _ => 0 }; public void Dispose() { _writer.Dispose(); } }