using System;
using System.IO;
using System.Linq;
using BepInEx.Logging;
namespace TravelEar;
///
/// A per-launch copy of the mod's log lines (and every warning or error from any source) under
/// %LOCALAPPDATA%\TravelEar\logs\game-<timestamp>.log. BepInEx overwrites
/// LogOutput.log on every launch, so a second short launch erases the run before it
/// (M3 run 5); these files survive. The newest are kept.
///
internal sealed class SessionLog : ILogListener
{
private const int Keep = 12;
private static SessionLog _instance;
private readonly StreamWriter _writer;
private readonly object _lock = new();
public static string Directory =>
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "TravelEar", "logs");
public string FilePath { get; }
public LogLevel LogLevelFilter => LogLevel.All;
private SessionLog(string path)
{
FilePath = path;
_writer = new StreamWriter(new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read)) { AutoFlush = true };
}
/// Starts the per-launch file and registers it with BepInEx's logger; failures are logged and ignored.
public static void Start(ManualLogSource log)
{
try
{
System.IO.Directory.CreateDirectory(Directory);
foreach (var stale in new DirectoryInfo(Directory).GetFiles("game-*.log").OrderByDescending(f => f.Name).Skip(Keep - 1))
{
try { stale.Delete(); } catch (Exception) { /* best effort */ }
}
var path = Path.Combine(Directory, $"game-{DateTime.Now:yyyyMMdd-HHmmss}.log");
_instance = new SessionLog(path);
Logger.Listeners.Add(_instance);
log.LogInfo($"Session log: {path}");
}
catch (Exception e)
{
log.LogWarning($"Session log: not started ({e.Message}); BepInEx/LogOutput.log is the only copy.");
}
}
public static void Stop()
{
var instance = _instance;
_instance = null;
if (instance is null) return;
try { Logger.Listeners.Remove(instance); } catch (Exception) { /* shutting down */ }
instance.Dispose();
}
public void LogEvent(object sender, LogEventArgs eventArgs)
{
try
{
var ours = eventArgs.Source?.SourceName == Plugin.Name;
if (!ours && (eventArgs.Level & (LogLevel.Warning | LogLevel.Error | LogLevel.Fatal)) == 0) return;
lock (_lock)
{
_writer.Write(DateTime.Now.ToString("HH:mm:ss.fff "));
_writer.WriteLine(eventArgs.ToString().TrimEnd());
}
}
catch (Exception)
{
// Never let the log copy disturb the game.
}
}
public void Dispose()
{
lock (_lock)
{
try { _writer.Dispose(); } catch (Exception) { /* best effort */ }
}
}
}