Files
sharpemu/src/SharpEmu.Logging/FileLogSink.cs
T
Spooks d43edc865a Agent/fix gen5 thread agc compat (#130)
* Fix Gen5 thread and AGC compatibility

* Trim compatibility comments

* Report selected Vulkan GPU

* Clean up CPU title label

* Improve emulator frame pacing and performance

* Regenerate package locks with pinned SDK

---------

Co-authored-by: Spooks4576 <Spooks4576@users.noreply.github.com>
2026-07-14 14:41:42 +03:00

138 lines
3.4 KiB
C#

// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using System.IO;
using System.Text;
namespace SharpEmu.Logging;
public sealed class FileLogSink : ISharpEmuLogSink, IDisposable
{
private static readonly TimeSpan FlushInterval = TimeSpan.FromMilliseconds(500);
private readonly object _sync = new();
private readonly StreamWriter _writer;
private readonly Timer _flushTimer;
private bool _disposed;
public FileLogSink(string path, bool append = true, bool includeTimestamp = true)
{
ArgumentException.ThrowIfNullOrWhiteSpace(path);
var directory = Path.GetDirectoryName(path);
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
var fileStream = new FileStream(
path,
append ? FileMode.Append : FileMode.Create,
FileAccess.Write,
FileShare.Read,
bufferSize: 65536,
FileOptions.SequentialScan);
_writer = new StreamWriter(fileStream, Encoding.UTF8, bufferSize: 65536)
{
AutoFlush = false
};
IncludeTimestamp = includeTimestamp;
_flushTimer = new Timer(
static state => ((FileLogSink)state!).FlushBuffered(),
this,
FlushInterval,
FlushInterval);
}
public bool IncludeTimestamp { get; set; }
public void Write(in LogEntry entry)
{
lock (_sync)
{
if (_disposed)
{
return;
}
if (IncludeTimestamp)
{
_writer.Write('[');
_writer.Write(entry.Timestamp.ToString("yyyy-MM-dd HH:mm:ss.fff"));
_writer.Write(']');
}
_writer.Write('[');
_writer.Write(ToLevelLabel(entry.Level));
_writer.Write(']');
_writer.Write('[');
_writer.Write(entry.Category);
_writer.Write(']');
_writer.Write(' ');
_writer.Write(entry.SourceFileName);
if (entry.SourceLine > 0)
{
_writer.Write(':');
_writer.Write(entry.SourceLine);
}
_writer.Write(' ');
_writer.WriteLine(entry.Message);
if (entry.Exception is not null)
{
_writer.WriteLine(entry.Exception);
}
if (entry.Level >= LogLevel.Error)
{
_writer.Flush();
}
}
}
private void FlushBuffered()
{
lock (_sync)
{
if (_disposed)
{
return;
}
_writer.Flush();
}
}
public void Dispose()
{
_flushTimer.Dispose();
lock (_sync)
{
if (_disposed)
{
return;
}
_disposed = true;
_writer.Flush();
_writer.Dispose();
}
}
private static string ToLevelLabel(LogLevel level) => level switch
{
LogLevel.Trace => "TRACE",
LogLevel.Debug => "DEBUG",
LogLevel.Info => "INFO",
LogLevel.Warning => "WARNING",
LogLevel.Error => "ERROR",
LogLevel.Critical => "CRITICAL",
_ => "LOG",
};
}