diff --git a/Dalamud/EntryPoint.cs b/Dalamud/EntryPoint.cs index 33e09e221..26ff396d1 100644 --- a/Dalamud/EntryPoint.cs +++ b/Dalamud/EntryPoint.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Dalamud.Configuration.Internal; using Dalamud.Logging.Internal; +using Dalamud.Logging.Retention; using Dalamud.Plugin.Internal; using Dalamud.Support; using Dalamud.Utility; @@ -88,58 +89,35 @@ public sealed class EntryPoint var logFileName = logName.IsNullOrEmpty() ? "dalamud" : $"dalamud-{logName}"; #if DEBUG - var logPath = Path.Combine(baseDirectory, $"{logFileName}.log"); - var oldPath = Path.Combine(baseDirectory, $"{logFileName}.old.log"); - var oldPathOld = Path.Combine(baseDirectory, $"{logFileName}.log.old"); + var logPath = new FileInfo(Path.Combine(baseDirectory, $"{logFileName}.log")); + var oldPath = new FileInfo(Path.Combine(baseDirectory, $"{logFileName}.old.log")); #else var logPath = Path.Combine(baseDirectory, "..", "..", "..", $"{logFileName}.log"); var oldPath = Path.Combine(baseDirectory, "..", "..", "..", $"{logFileName}.old.log"); - var oldPathOld = Path.Combine(baseDirectory, "..", "..", "..", $"{logFileName}.log.old"); #endif Log.CloseAndFlush(); - -#if DEBUG - var oldFileOld = new FileInfo(oldPathOld); - if (oldFileOld.Exists) - { - var oldFile = new FileInfo(oldPath); - if (oldFile.Exists) - oldFileOld.Delete(); - else - oldFileOld.MoveTo(oldPath); - } - CullLogFile(logPath, 1 * 1024 * 1024, oldPath, 10 * 1024 * 1024); + RetentionBehaviour behaviour; +#if DEBUG + behaviour = new DebugRetentionBehaviour(); #else - try - { - if (File.Exists(logPath)) - File.Delete(logPath); - - if (File.Exists(oldPath)) - File.Delete(oldPath); - - if (File.Exists(oldPathOld)) - File.Delete(oldPathOld); - } - catch - { - // ignored - } + behaviour = new ReleaseRetentionBehaviour(); #endif + behaviour.Apply(logPath, oldPath); + var config = new LoggerConfiguration() .WriteTo.Sink(SerilogEventSink.Instance) .MinimumLevel.ControlledBy(LogLevelSwitch); if (logSynchronously) { - config = config.WriteTo.File(logPath, fileSizeLimitBytes: null); + config = config.WriteTo.File(logPath.FullName, fileSizeLimitBytes: null); } else { config = config.WriteTo.Async(a => a.File( - logPath, + logPath.FullName, fileSizeLimitBytes: null, buffered: false, flushToDiskInterval: TimeSpan.FromSeconds(1))); @@ -265,86 +243,6 @@ public sealed class EntryPoint } } - /// - /// Trim existing log file to a specified length, and optionally move the excess data to another file. - /// - /// Target log file to trim. - /// Maximum size of target log file. - /// .old file to move excess data to. - /// Maximum size of .old file. - private static void CullLogFile(string logPath, int logMaxSize, string oldPath, int oldMaxSize) - { - var logFile = new FileInfo(logPath); - var oldFile = new FileInfo(oldPath); - var targetFiles = new[] - { - (logFile, logMaxSize), - (oldFile, oldMaxSize), - }; - var buffer = new byte[4096]; - - try - { - if (!logFile.Exists) - logFile.Create().Close(); - - // 1. Move excess data from logFile to oldFile - if (logFile.Length > logMaxSize) - { - using var reader = logFile.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite); - using var writer = oldFile.Open(FileMode.Append, FileAccess.Write, FileShare.ReadWrite); - - var amountToMove = (int)Math.Min(logFile.Length - logMaxSize, oldMaxSize); - reader.Seek(-(logMaxSize + amountToMove), SeekOrigin.End); - - for (var i = 0; i < amountToMove; i += buffer.Length) - writer.Write(buffer, 0, reader.Read(buffer, 0, Math.Min(buffer.Length, amountToMove - i))); - } - - // 2. Cull each of .log and .old files - foreach (var (file, maxSize) in targetFiles) - { - if (!file.Exists || file.Length <= maxSize) - continue; - - using var reader = file.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite); - using var writer = file.Open(FileMode.Open, FileAccess.Write, FileShare.ReadWrite); - - reader.Seek(file.Length - maxSize, SeekOrigin.Begin); - for (int read; (read = reader.Read(buffer, 0, buffer.Length)) > 0;) - writer.Write(buffer, 0, read); - - writer.SetLength(maxSize); - } - } - catch (Exception ex) - { - if (ex is IOException) - { - foreach (var (file, _) in targetFiles) - { - try - { - if (file.Exists) - file.Delete(); - } - catch (Exception ex2) - { - Log.Error(ex2, "Failed to delete {file}", file.FullName); - } - } - } - - Log.Error(ex, "Log cull failed"); - - /* - var caption = "XIVLauncher Error"; - var message = $"Log cull threw an exception: {ex.Message}\n{ex.StackTrace ?? string.Empty}"; - _ = MessageBoxW(IntPtr.Zero, message, caption, MessageBoxType.IconError | MessageBoxType.Ok); - */ - } - } - private static void OnUnhandledException(object sender, UnhandledExceptionEventArgs args) { switch (args.ExceptionObject) diff --git a/Dalamud/Logging/Retention/DebugRetentionBehaviour.cs b/Dalamud/Logging/Retention/DebugRetentionBehaviour.cs new file mode 100644 index 000000000..719ff8ece --- /dev/null +++ b/Dalamud/Logging/Retention/DebugRetentionBehaviour.cs @@ -0,0 +1,15 @@ +using System.IO; + +namespace Dalamud.Logging.Retention; + +/// +/// Class implementing log retention behaviour for debug builds. +/// +internal class DebugRetentionBehaviour : RetentionBehaviour +{ + /// + public override void Apply(FileInfo logFile, FileInfo rolloverFile) + { + CullLogFile(logFile, 1 * 1024 * 1024, rolloverFile, 10 * 1024 * 1024); + } +} diff --git a/Dalamud/Logging/Retention/ReleaseRetentionBehaviour.cs b/Dalamud/Logging/Retention/ReleaseRetentionBehaviour.cs new file mode 100644 index 000000000..79f272c46 --- /dev/null +++ b/Dalamud/Logging/Retention/ReleaseRetentionBehaviour.cs @@ -0,0 +1,15 @@ +using System.IO; + +namespace Dalamud.Logging.Retention; + +/// +/// Class implementing log retention behaviour for release builds. +/// +internal class ReleaseRetentionBehaviour : RetentionBehaviour +{ + /// + public override void Apply(FileInfo logFile, FileInfo rolloverFile) + { + CullLogFile(logFile, 0, rolloverFile, 10 * 1024 * 1024); + } +} diff --git a/Dalamud/Logging/Retention/RetentionBehaviour.cs b/Dalamud/Logging/Retention/RetentionBehaviour.cs new file mode 100644 index 000000000..66c4c5f97 --- /dev/null +++ b/Dalamud/Logging/Retention/RetentionBehaviour.cs @@ -0,0 +1,91 @@ +using System; +using System.IO; + +using Serilog; + +namespace Dalamud.Logging.Retention; + +/// +/// Class implementing retention behaviour for log files. +/// +internal abstract class RetentionBehaviour +{ + /// + /// Apply the specified retention behaviour to log files. + /// + /// The regular log file path. + /// The rollover "old" log file path. + public abstract void Apply(FileInfo logFile, FileInfo rolloverFile); + + /// + /// Trim existing log file to a specified length, and optionally move the excess data to another file. + /// + /// Target log file to trim. + /// Maximum size of target log file. + /// .old file to move excess data to. + /// Maximum size of .old file. + protected static void CullLogFile(FileInfo logFile, int logMaxSize, FileInfo oldFile, int oldMaxSize) + { + var targetFiles = new[] + { + (logFile, logMaxSize), + (oldFile, oldMaxSize), + }; + var buffer = new byte[4096]; + + try + { + if (!logFile.Exists) + logFile.Create().Close(); + + // 1. Move excess data from logFile to oldFile + if (logFile.Length > logMaxSize) + { + using var reader = logFile.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + using var writer = oldFile.Open(FileMode.Append, FileAccess.Write, FileShare.ReadWrite); + + var amountToMove = (int)Math.Min(logFile.Length - logMaxSize, oldMaxSize); + reader.Seek(-(logMaxSize + amountToMove), SeekOrigin.End); + + for (var i = 0; i < amountToMove; i += buffer.Length) + writer.Write(buffer, 0, reader.Read(buffer, 0, Math.Min(buffer.Length, amountToMove - i))); + } + + // 2. Cull each of .log and .old files + foreach (var (file, maxSize) in targetFiles) + { + if (!file.Exists || file.Length <= maxSize) + continue; + + using var reader = file.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + using var writer = file.Open(FileMode.Open, FileAccess.Write, FileShare.ReadWrite); + + reader.Seek(file.Length - maxSize, SeekOrigin.Begin); + for (int read; (read = reader.Read(buffer, 0, buffer.Length)) > 0;) + writer.Write(buffer, 0, read); + + writer.SetLength(maxSize); + } + } + catch (Exception ex) + { + if (ex is IOException) + { + foreach (var (file, _) in targetFiles) + { + try + { + if (file.Exists) + file.Delete(); + } + catch (Exception ex2) + { + Log.Error(ex2, "Failed to delete {file}", file.FullName); + } + } + } + + Log.Error(ex, "Log cull failed"); + } + } +}