Files
StarterNG/StarterNG/Infrastructure/Adapters/FileDiagnosticsLog.cs
maj00r 07987a60a9 Write settings, presets and favourites whole or not at all
A failed write leaves the old file in place. Settings that could not be read
are not saved over. UI exceptions reach the log. Started from a shortcut set
up elsewhere, the starter moves to its own folder when only that one holds a
game.
2026-11-01 16:06:15 +01:00

157 lines
3.7 KiB
C#

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using StarterNG.Application.Abstractions;
namespace StarterNG.Infrastructure.Adapters;
public sealed class FileDiagnosticsLog : IDiagnosticsLog
{
private readonly IFileSystem _files;
private readonly IClock _clock;
private readonly string _logPath;
private readonly object _gate = new();
private readonly List<string> _lines = new();
private StreamWriter? _writer;
private bool _writerFailed;
private bool _previousKept;
public FileDiagnosticsLog(IFileSystem files, IClock clock, IGamePaths paths)
{
_files = files;
_clock = clock;
_logPath = paths.DiagnosticsLog;
}
public string LogPath => _logPath;
public string Text
{
get { lock (_gate) return string.Join(Environment.NewLine, _lines); }
}
public void Log(string message)
{
string line = $"{_clock.Now.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture)} {message}";
lock (_gate)
{
_lines.Add(line);
Append(line);
}
}
public void Log(string context, Exception exception) =>
Log($"{context}: {exception.GetType().Name}: {exception.Message}");
public void Log(IEnumerable<string> messages)
{
foreach (string message in messages)
Log(message);
}
public void Clear()
{
lock (_gate)
{
_lines.Clear();
CloseWriter();
try
{
if (_files.FileExists(_logPath))
_files.DeleteFile(_logPath);
}
catch
{
}
}
}
public void Flush()
{
lock (_gate)
{
if (_lines.Count == 0)
return;
try
{
_writer?.Flush();
}
catch (Exception ex)
{
_writerFailed = true;
_lines.Add($"log flush failed: {ex.GetType().Name}: {ex.Message}");
}
if (!_writerFailed && _writer is not null)
return;
try
{
_files.CreateDirectory(Path.GetDirectoryName(_logPath)!);
if (KeepPreviousSession())
_files.WriteAllLines(_logPath, _lines);
}
catch
{
}
}
}
// What the last session left, a crash's traces above all, is what this log is
// read for: one copy of it goes aside before the first entry of this one. False
// when it could not, and the log is then added to instead of started over.
private bool KeepPreviousSession()
{
if (_previousKept)
return true;
try
{
if (_files.FileExists(_logPath))
_files.CopyFile(_logPath, _logPath + ".1", overwrite: true);
return _previousKept = true;
}
catch
{
return false;
}
}
private void Append(string line)
{
if (_writerFailed)
return;
try
{
if (_writer is null)
{
_files.CreateDirectory(Path.GetDirectoryName(_logPath)!);
_writer = new StreamWriter(_logPath, append: !KeepPreviousSession()) { AutoFlush = true };
}
_writer.WriteLine(line);
}
catch
{
_writerFailed = true;
CloseWriter();
}
}
private void CloseWriter()
{
try
{
_writer?.Dispose();
}
catch
{
}
_writer = null;
}
}