using System; using System.IO; using System.Linq; namespace Engine.Core.Debug; public class RotatingFileLogger : ILogger { public readonly FileLogger FileLogger = null!; public readonly string Directory = string.Empty; public readonly int RotateLength = 3; public RotatingFileLogger(string directory, string namePrefix, string nameSuffix = "", int rotateLength = 3) { RotateLength = rotateLength; string fileName = Path.Combine(directory, namePrefix); if (!string.IsNullOrWhiteSpace(nameSuffix)) fileName += $"_{nameSuffix}"; bool isRelativePath = Path.GetFullPath(fileName).CompareTo(fileName) != 0; if (isRelativePath) fileName = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName)); if (File.Exists($"{fileName}.log")) RenameExistingLogs(fileName, RotateLength); FileLogger = new(fileName); Directory = Path.GetDirectoryName(fileName) ?? throw new("Unexpected error on getting directory of logger path"); RotateLastLogs(Directory, namePrefix, RotateLength); } private static void RenameExistingLogs(string filePath, int rotateLength) { for (int i = rotateLength - 1; i >= 0; i--) { string source = i == 0 ? $"{filePath}.log" : $"{filePath}_{i}.log"; string dest = $"{filePath}_{i + 1}.log"; if (!File.Exists(source)) continue; if (File.Exists(dest)) File.Delete(dest); File.Move(source, dest); } } private static void RotateLastLogs(string directory, string prefix, int rotateLength) { IOrderedEnumerable logs = System.IO.Directory.GetFiles(directory, $"{prefix}*.log") .OrderBy(File.GetCreationTime); foreach (string file in logs.Skip(rotateLength)) try { ILogger.Shared.Log($"Removing log file located at \"{file}\" during rotation."); File.Delete(file); } catch (Exception e) { ILogger.Shared.LogException($"Failed to rotate log file at \"{file}\"", e); } } public ILogger.Level FilterLevel { get => FileLogger.FilterLevel; set => FileLogger.FilterLevel = value; } public void Log(string message, ILogger.Level level = ILogger.Level.Info, bool force = false) => FileLogger.Log(message, level, force); }