bloxstrap/Bloxstrap/Logger.cs
pizzaboxer e0fcbaab2f
Fix race condition with duplicate instances
turns out it happens so quickly that not even having a file existence check right before opening it is sufficient enough
2023-07-27 22:17:00 +01:00

114 lines
3.5 KiB
C#

namespace Bloxstrap
{
// https://stackoverflow.com/a/53873141/11852173
public class Logger
{
private readonly SemaphoreSlim _semaphore = new(1, 1);
private FileStream? _filestream;
public readonly List<string> Backlog = new();
public bool Initialized = false;
public string? FileLocation;
public void Initialize(bool useTempDir = false)
{
const string LOG_IDENT = "Logger::Initialize";
string directory = useTempDir ? Path.Combine(Paths.LocalAppData, "Temp") : Path.Combine(Paths.Base, "Logs");
string timestamp = DateTime.UtcNow.ToString("yyyyMMdd'T'HHmmss'Z'");
string filename = $"{App.ProjectName}_{timestamp}.log";
string location = Path.Combine(directory, filename);
WriteLine(LOG_IDENT, $"Initializing at {location}");
if (Initialized)
{
WriteLine(LOG_IDENT, "Failed to initialize because logger is already initialized");
return;
}
Directory.CreateDirectory(directory);
if (File.Exists(location))
{
WriteLine(LOG_IDENT, "Failed to initialize because log file already exists");
return;
}
try
{
_filestream = File.Open(location, FileMode.Create, FileAccess.Write, FileShare.Read);
}
catch (IOException)
{
WriteLine(LOG_IDENT, "Failed to initialize because log file already exists");
}
Initialized = true;
if (Backlog.Count > 0)
WriteToLog(string.Join("\r\n", Backlog));
WriteLine(LOG_IDENT, "Finished initializing!");
FileLocation = location;
// clean up any logs older than a week
if (Paths.Initialized && Directory.Exists(Paths.Logs))
{
foreach (FileInfo log in new DirectoryInfo(Paths.Logs).GetFiles())
{
if (log.LastWriteTimeUtc.AddDays(7) > DateTime.UtcNow)
continue;
WriteLine(LOG_IDENT, $"Cleaning up old log file '{log.Name}'");
log.Delete();
}
}
}
private void WriteLine(string message)
{
string timestamp = DateTime.UtcNow.ToString("s") + "Z";
string outcon = $"{timestamp} {message}";
string outlog = outcon.Replace(Paths.UserProfile, "%UserProfile%");
Debug.WriteLine(outcon);
WriteToLog(outlog);
}
public void WriteLine(string identifier, string message) => WriteLine($"[{identifier}] {message}");
public void WriteException(string identifier, Exception ex)
{
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
WriteLine($"[{identifier}] {ex}");
}
private async void WriteToLog(string message)
{
if (!Initialized)
{
Backlog.Add(message);
return;
}
try
{
await _semaphore.WaitAsync();
await _filestream!.WriteAsync(Encoding.UTF8.GetBytes($"{message}\r\n"));
_ = _filestream.FlushAsync();
}
finally
{
_semaphore.Release();
}
}
}
}