blizzless-diiis/src/DiIiS-NA/Core/Logging/FileTarget.cs
2023-02-10 09:56:30 -08:00

146 lines
5.2 KiB
C#

using System;
using System.Collections.Concurrent;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;
namespace DiIiS_NA.Core.Logging
{
public class FileTarget : LogTarget, IDisposable
{
private readonly string _fileName;
private readonly string _filePath;
private readonly string _fileTimestamp;
private FileStream _fileStream;
private StreamWriter _logStream;
private int _fileIndex;
private ConcurrentQueue<Action> TaskQueue;
private Thread LoggerThread;
/// <param name="fileName">Filename of the logfile.</param>
/// <param name="minLevel">Minimum level of messages to emit</param>
/// <param name="maxLevel">Maximum level of messages to emit</param>
/// <param name="includeTimeStamps">Include timestamps in log?</param>
/// <param name="reset">Reset log file on application startup?</param>
public FileTarget(string fileName, Logger.Level minLevel, Logger.Level maxLevel, bool includeTimeStamps, string timeStampFormat, bool reset = false)
{
MinimumLevel = minLevel;
MaximumLevel = maxLevel;
IncludeTimeStamps = includeTimeStamps;
TimeStampFormat = timeStampFormat;
_fileName = fileName;
_fileTimestamp = DateTime.Now.ToString("yyyy-MM-dd_HH-mm");
_filePath = $"{LogConfig.Instance.LoggingRoot}/{_fileTimestamp}/{_fileName}";
_fileIndex = 0;
if (!Directory.Exists(LogConfig.Instance.LoggingRoot)) // create logging directory if it does not exist yet.
Directory.CreateDirectory(LogConfig.Instance.LoggingRoot);
if (!Directory.Exists($"{LogConfig.Instance.LoggingRoot}/{_fileTimestamp}")) // create logging directory if it does not exist yet.
Directory.CreateDirectory($"{LogConfig.Instance.LoggingRoot}/{_fileTimestamp}");
_fileStream = new FileStream(_filePath, reset ? FileMode.Create : FileMode.Append, FileAccess.Write, FileShare.Read); // init the file stream.
_logStream = new StreamWriter(_fileStream) { AutoFlush = true }; // init the stream writer.
TaskQueue = new ConcurrentQueue<Action>();
LoggerThread = new Thread(CheckQueue) { Name = "Logger", IsBackground = true };
LoggerThread.Start();
}
public void CheckQueue()
{
while (true)
{
if (TaskQueue.TryDequeue(out var action))
action.Invoke();
Thread.Sleep(1);
}
}
/// <summary>
/// Replace the colors from AnsiColor so they do not appear in the log file.
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
private string NoColors(string message) => Regex.Replace(message, @"\$\[\[?([\w\W\d\s_\-\/]+)\]\]?\$", "$1");
/// <param name="level">Log level.</param>
/// <param name="logger">Source of the log message.</param>
/// <param name="message">Log message.</param>
public override void LogMessage(Logger.Level level, string logger, string message)
{
TaskQueue.Enqueue(() =>
{
message = NoColors(message);
var timeStamp = IncludeTimeStamps ? "[" + DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss.fff") + "] " : "";
if (!_disposed) // make sure we're not disposed.
{
/*
if (this._fileStream.Length >= 20971520) //20 MB limit
{
//System.IO.File.Move(this._filePath, string.Format("{0}.{1}", this._filePath, this._fileIndex));
//this._fileIndex++;
this._fileStream = new FileStream(_filePath, FileMode.Create, FileAccess.Write, FileShare.Read); // init the file stream.
this._logStream = new StreamWriter(this._fileStream) { AutoFlush = true }; // init the stream writer.
}
//*/
_logStream.WriteLine(level > Logger.Level.ChatMessage
? $"{timeStamp}[{level.ToString(),5}] [{logger}]: {message}"
: $"{timeStamp}{message}");
}
});
}
/// <param name="level">Log level.</param>
/// <param name="logger">Source of the log message.</param>
/// <param name="message">Log message.</param>
/// <param name="exception">Exception to be included with log message.</param>
public override void LogException(Logger.Level level, string logger, string message, Exception exception)
{
TaskQueue.Enqueue(() =>
{
message = NoColors(message);
var timeStamp = IncludeTimeStamps ? "[" + DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss.fff") + "] " : "";
if (!_disposed) // make sure we're not disposed.
{
_logStream.WriteLine(
$"{timeStamp}[{level.ToString(),5}] [{logger}]: {message} - [Exception] {exception}");
}
});
}
#region de-ctor
// IDisposable pattern: http://msdn.microsoft.com/en-us/library/fs2xkftw%28VS.80%29.aspx
private bool _disposed = false;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this); // Take object out the finalization queue to prevent finalization code for it from executing (~FileTarget).
}
private void Dispose(bool disposing)
{
if (_disposed) return; // if already disposed, just return
if (disposing) // only dispose managed resources if we're called from directly or in-directly from user code.
{
_logStream.Close();
_logStream.Dispose();
_fileStream.Close();
_fileStream.Dispose();
}
_logStream = null;
_fileStream = null;
_disposed = true;
}
~FileTarget() { Dispose(false); } // finalizer called by the runtime. we should only dispose unmanaged objects and should NOT reference managed ones.
#endregion
}
}