using IllusionInjector.Logging.Printers; using IllusionPlugin; using IllusionPlugin.Logging; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using LoggerBase = IllusionPlugin.Logging.Logger; namespace IllusionInjector.Logging { internal static class Logger { private static LoggerBase _log; internal static LoggerBase log { get { if (_log == null) _log = new StandardLogger("IPA"); return _log; } } } public class StandardLogger : LoggerBase { private static readonly IReadOnlyList defaultPrinters = new List() { new ColoredConsolePrinter() { Filter = LogLevel.DebugOnly, Color = ConsoleColor.Green, }, new ColoredConsolePrinter() { Filter = LogLevel.InfoOnly, Color = ConsoleColor.White, }, new ColoredConsolePrinter() { Filter = LogLevel.WarningOnly, Color = ConsoleColor.Yellow, }, new ColoredConsolePrinter() { Filter = LogLevel.ErrorOnly, Color = ConsoleColor.Red, }, new ColoredConsolePrinter() { Filter = LogLevel.CriticalOnly, Color = ConsoleColor.Magenta, }, new GlobalLogFilePrinter() }; private string logName; private static LogLevel showFilter = LogLevel.InfoUp; private static bool showSourceClass = true; public static LogLevel PrintFilter { get => showFilter; set => showFilter = value; } private List printers = new List(defaultPrinters); static StandardLogger() { if (ModPrefs.GetBool("IPA", "PrintDebug", false, true)) showFilter = LogLevel.All; showSourceClass = ModPrefs.GetBool("IPA", "DebugShowCallSource", false, true); } internal StandardLogger(string name) { logName = name; printers.Add(new PluginLogFilePrinter(name)); if (_logThread == null || !_logThread.IsAlive) { _logThread = new Thread(LogThread); _logThread.Start(); } } public override void Log(Level level, string message) { _logQueue.Add(new LogMessage { level = level, message = message, logger = this, time = DateTime.Now }); } public override void Debug(string message) { // add source to message var stfm = new StackTrace().GetFrame(1).GetMethod(); if (showSourceClass) base.Debug($"{{{stfm.DeclaringType.FullName}::{stfm.Name}}} {message}"); else base.Debug(message); } internal struct LogMessage { public Level level; public StandardLogger logger; public string message; public DateTime time; } private static BlockingCollection _logQueue = new BlockingCollection(); private static Thread _logThread; private static void LogThread() { HashSet started = new HashSet(); while (_logQueue.TryTake(out LogMessage msg, Timeout.Infinite)) { foreach (var printer in msg.logger.printers) { try { if (((byte)msg.level & (byte)printer.Filter) != 0) { if (!started.Contains(printer)) { printer.StartPrint(); started.Add(printer); } printer.Print(msg.level, msg.time, msg.logger.logName, msg.message); } } catch (Exception e) { Console.WriteLine($"printer errored {e}"); } } if (_logQueue.Count == 0) { foreach (var printer in started) { try { printer.EndPrint(); } catch (Exception e) { Console.WriteLine($"printer errored {e}"); } } started.Clear(); } } } public static void StopLogThread() { _logQueue.CompleteAdding(); _logThread.Join(); } } }