96 lines
3.1 KiB
C#
96 lines
3.1 KiB
C#
using System.IO.Compression;
|
|
using NLog;
|
|
using NLog.Config;
|
|
using NLog.Layouts;
|
|
using OliverBooth.Logging;
|
|
using LogLevel = NLog.LogLevel;
|
|
|
|
namespace OliverBooth.Services;
|
|
|
|
/// <summary>
|
|
/// Represents a class which implements a logging service that supports multiple log targets.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This class implements a logging structure similar to that of Minecraft, where historic logs are compressed to a .gz and
|
|
/// the latest log is found in <c>logs/latest.log</c>.
|
|
/// </remarks>
|
|
internal sealed class LoggingService : BackgroundService
|
|
{
|
|
private const string LogFileName = "logs/latest.log";
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="LoggingService" /> class.
|
|
/// </summary>
|
|
public LoggingService()
|
|
{
|
|
LogFile = new FileInfo(LogFileName);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the log file.
|
|
/// </summary>
|
|
/// <value>The log file.</value>
|
|
public FileInfo LogFile { get; set; }
|
|
|
|
/// <summary>
|
|
/// Archives any existing log files.
|
|
/// </summary>
|
|
public async Task ArchiveLogFilesAsync(bool archiveToday = true)
|
|
{
|
|
var latestFile = new FileInfo(LogFile.FullName);
|
|
if (!latestFile.Exists) return;
|
|
|
|
DateTime lastWrite = latestFile.LastWriteTime;
|
|
string lastWriteDate = $"{lastWrite:yyyy-MM-dd}";
|
|
var version = 0;
|
|
string name;
|
|
|
|
if (!archiveToday && lastWrite.Date == DateTime.Today) return;
|
|
|
|
while (File.Exists(name = Path.Combine(LogFile.Directory!.FullName, $"{lastWriteDate}-{++version}.log.gz")))
|
|
{
|
|
// body ignored
|
|
}
|
|
|
|
await using (FileStream source = latestFile.OpenRead())
|
|
{
|
|
await using FileStream output = File.Create(name);
|
|
await using var gzip = new GZipStream(output, CompressionMode.Compress);
|
|
await source.CopyToAsync(gzip);
|
|
}
|
|
|
|
latestFile.Delete();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
protected override Task ExecuteAsync(CancellationToken stoppingToken)
|
|
{
|
|
LogFile.Directory?.Create();
|
|
|
|
LogManager.Setup(builder => builder.SetupExtensions(extensions =>
|
|
{
|
|
extensions.RegisterLayoutRenderer("TheTime", info => info.TimeStamp.ToString("HH:mm:ss"));
|
|
extensions.RegisterLayoutRenderer("ServiceName", info => info.LoggerName);
|
|
}));
|
|
|
|
Layout? layout = Layout.FromString("[${TheTime} ${level:uppercase=true}] [${ServiceName}] ${message}");
|
|
var config = new LoggingConfiguration();
|
|
var fileLogger = new LogFileTarget("FileLogger", this) { Layout = layout };
|
|
var consoleLogger = new ColorfulConsoleTarget("ConsoleLogger") { Layout = layout };
|
|
|
|
#if DEBUG
|
|
LogLevel minLevel = LogLevel.Debug;
|
|
#else
|
|
LogLevel minLevel = LogLevel.Info;
|
|
if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("ENABLE_DEBUG_LOGGING")))
|
|
minLevel = LogLevel.Debug;
|
|
#endif
|
|
config.AddRule(minLevel, LogLevel.Fatal, consoleLogger);
|
|
config.AddRule(minLevel, LogLevel.Fatal, fileLogger);
|
|
|
|
LogManager.Configuration = config;
|
|
|
|
return ArchiveLogFilesAsync();
|
|
}
|
|
}
|