fix: move log retention out of EntryPoint.cs, redo retention behaviour for release

This commit is contained in:
goat 2023-06-19 19:19:04 +02:00
parent 418a2567a9
commit b901ad5aff
No known key found for this signature in database
GPG key ID: 49E2AA8C6A76498B
4 changed files with 132 additions and 113 deletions

View file

@ -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
}
}
/// <summary>
/// Trim existing log file to a specified length, and optionally move the excess data to another file.
/// </summary>
/// <param name="logPath">Target log file to trim.</param>
/// <param name="logMaxSize">Maximum size of target log file.</param>
/// <param name="oldPath">.old file to move excess data to.</param>
/// <param name="oldMaxSize">Maximum size of .old file.</param>
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)

View file

@ -0,0 +1,15 @@
using System.IO;
namespace Dalamud.Logging.Retention;
/// <summary>
/// Class implementing log retention behaviour for debug builds.
/// </summary>
internal class DebugRetentionBehaviour : RetentionBehaviour
{
/// <inheritdoc/>
public override void Apply(FileInfo logFile, FileInfo rolloverFile)
{
CullLogFile(logFile, 1 * 1024 * 1024, rolloverFile, 10 * 1024 * 1024);
}
}

View file

@ -0,0 +1,15 @@
using System.IO;
namespace Dalamud.Logging.Retention;
/// <summary>
/// Class implementing log retention behaviour for release builds.
/// </summary>
internal class ReleaseRetentionBehaviour : RetentionBehaviour
{
/// <inheritdoc/>
public override void Apply(FileInfo logFile, FileInfo rolloverFile)
{
CullLogFile(logFile, 0, rolloverFile, 10 * 1024 * 1024);
}
}

View file

@ -0,0 +1,91 @@
using System;
using System.IO;
using Serilog;
namespace Dalamud.Logging.Retention;
/// <summary>
/// Class implementing retention behaviour for log files.
/// </summary>
internal abstract class RetentionBehaviour
{
/// <summary>
/// Apply the specified retention behaviour to log files.
/// </summary>
/// <param name="logFile">The regular log file path.</param>
/// <param name="rolloverFile">The rollover "old" log file path.</param>
public abstract void Apply(FileInfo logFile, FileInfo rolloverFile);
/// <summary>
/// Trim existing log file to a specified length, and optionally move the excess data to another file.
/// </summary>
/// <param name="logFile">Target log file to trim.</param>
/// <param name="logMaxSize">Maximum size of target log file.</param>
/// <param name="oldFile">.old file to move excess data to.</param>
/// <param name="oldMaxSize">Maximum size of .old file.</param>
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");
}
}
}