@ -1 +1 @@ | |||||
6744afebdfdc05ced46858bdac21e047b0d6e43f | |||||
33cd6c2ad2a5a7958d22e4f89028cf896273055b |
@ -1,4 +1,4 @@ | |||||
<?xml version="1.0" encoding="utf-8"?> | <?xml version="1.0" encoding="utf-8"?> | ||||
<packages> | <packages> | ||||
<package id="Mono.Cecil" version="0.9.6.4" targetFramework="net35-client" requireReinstallation="true" /> | |||||
<package id="Mono.Cecil" version="0.9.6.4" targetFramework="net462" /> | |||||
</packages> | </packages> |
@ -0,0 +1,26 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace IllusionPlugin.Logging.Printers | |||||
{ | |||||
public class ColoredConsolePrinter : LogPrinter | |||||
{ | |||||
Logger.LogLevel filter = Logger.LogLevel.All; | |||||
public override Logger.LogLevel Filter { get => filter; set => filter = value; } | |||||
ConsoleColor color = Console.ForegroundColor; | |||||
public ConsoleColor Color { get => color; set => color = value; } | |||||
public override void Print(Logger.Level level, DateTime time, string logName, string message) | |||||
{ | |||||
if (((byte)level & (byte)Filter) == 0) return; | |||||
Console.ForegroundColor = color; | |||||
foreach (var line in message.Split(new string[] { "\n", Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)) | |||||
Console.WriteLine(string.Format(Logger.LogFormat, line, logName, time, level.ToString().ToUpper())); | |||||
Console.ResetColor(); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,55 @@ | |||||
using IllusionPlugin.Logging; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.IO; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
using zlib; | |||||
namespace IllusionInjector.Logging.Printers | |||||
{ | |||||
public class GlobalZFIlePrinter : LogPrinter | |||||
{ | |||||
public override IllusionPlugin.Logging.Logger.LogLevel Filter { get; set; } | |||||
private FileInfo fileInfo; | |||||
private StreamWriter fileWriter; | |||||
private static FileInfo GetFileInfo() | |||||
{ | |||||
var logsDir = new DirectoryInfo("Logs"); | |||||
logsDir.Create(); | |||||
var finfo = new FileInfo(Path.Combine(logsDir.FullName, $"{DateTime.Now:YYYY.MM.DD.HH.MM}.log.z")); | |||||
finfo.Create().Close(); | |||||
return finfo; | |||||
} | |||||
public GlobalZFIlePrinter() | |||||
{ | |||||
fileInfo = GetFileInfo(); | |||||
} | |||||
public override void StartPrint() | |||||
{ | |||||
fileWriter = new StreamWriter( | |||||
new ZOutputStream(fileInfo.Open(FileMode.Append, FileAccess.Write, FileShare.Read)) | |||||
{ | |||||
FlushMode = zlibConst.Z_FULL_FLUSH | |||||
}, | |||||
Encoding.UTF8 | |||||
); | |||||
} | |||||
public override void Print(IllusionPlugin.Logging.Logger.Level level, DateTime time, string logName, string message) | |||||
{ | |||||
foreach (var line in message.Split(new string[] { "\n", Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)) | |||||
fileWriter.WriteLine(string.Format("[{3} @ {2:HH:mm:ss} | {1}] {0}", line, logName, time, level.ToString().ToUpper())); | |||||
} | |||||
public override void EndPrint() | |||||
{ | |||||
fileWriter.Dispose(); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,48 @@ | |||||
using IllusionPlugin.Logging; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.IO; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace IllusionInjector.Logging.Printers | |||||
{ | |||||
public class PluginLogFilePrinter : LogPrinter | |||||
{ | |||||
public override IllusionPlugin.Logging.Logger.LogLevel Filter { get; set; } | |||||
private FileInfo fileInfo; | |||||
private StreamWriter fileWriter; | |||||
private static FileInfo GetFileInfo(string modName) | |||||
{ | |||||
var logsDir = new DirectoryInfo(Path.Combine("Logs",modName)); | |||||
logsDir.Create(); | |||||
var finfo = new FileInfo(Path.Combine(logsDir.FullName, $"{DateTime.Now:YYYY.MM.DD.HH.MM}.log")); | |||||
finfo.CreateText().Close(); | |||||
return finfo; | |||||
} | |||||
public PluginLogFilePrinter(string name) | |||||
{ | |||||
fileInfo = GetFileInfo(name); | |||||
} | |||||
public override void StartPrint() | |||||
{ | |||||
fileWriter = fileInfo.AppendText(); | |||||
} | |||||
public override void Print(IllusionPlugin.Logging.Logger.Level level, DateTime time, string logName, string message) | |||||
{ | |||||
foreach (var line in message.Split(new string[] { "\n", Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)) | |||||
fileWriter.WriteLine(string.Format("[{3} @ {2:HH:mm:ss}] {0}", line, logName, time, level.ToString().ToUpper())); | |||||
} | |||||
public override void EndPrint() | |||||
{ | |||||
fileWriter.Dispose(); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,134 @@ | |||||
using IllusionInjector.Logging.Printers; | |||||
using IllusionPlugin.Logging; | |||||
using IllusionPlugin.Logging.Printers; | |||||
using System; | |||||
using System.Collections.Concurrent; | |||||
using System.Collections.Generic; | |||||
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("IllusionInjector"); | |||||
return _log; | |||||
} | |||||
} | |||||
} | |||||
public class StandardLogger : LoggerBase | |||||
{ | |||||
private static readonly IReadOnlyList<LogPrinter> defaultPrinters = new List<LogPrinter>() | |||||
{ | |||||
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 GlobalZFIlePrinter() | |||||
}; | |||||
private string logName; | |||||
private static LogLevel showFilter = LogLevel.InfoUp; | |||||
public static LogLevel Filter { get => showFilter; set => showFilter = value; } | |||||
private List<LogPrinter> printers = new List<LogPrinter>(defaultPrinters); | |||||
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 | |||||
}); | |||||
} | |||||
internal struct LogMessage | |||||
{ | |||||
public Level level; | |||||
public StandardLogger logger; | |||||
public string message; | |||||
public DateTime time; | |||||
} | |||||
private static BlockingCollection<LogMessage> _logQueue = new BlockingCollection<LogMessage>(); | |||||
private static Thread _logThread; | |||||
private static void LogThread() | |||||
{ | |||||
HashSet<LogPrinter> started = new HashSet<LogPrinter>(); | |||||
while (_logQueue.TryTake(out LogMessage msg, Timeout.Infinite)) { | |||||
foreach (var printer in msg.logger.printers) | |||||
{ | |||||
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); | |||||
} | |||||
} | |||||
if (_logQueue.Count == 0) | |||||
{ | |||||
foreach (var printer in started) | |||||
printer.EndPrint(); | |||||
started.Clear(); | |||||
} | |||||
} | |||||
} | |||||
public static void StopLogThread() | |||||
{ | |||||
_logQueue.CompleteAdding(); | |||||
_logThread.Join(); | |||||
} | |||||
} | |||||
} |
@ -1 +1 @@ | |||||
26a9be3d697b3ac9c2a8af98062cb97cf748184e | |||||
524c2a9e58911edfdaf36eb5bf255b181a39f814 |
@ -0,0 +1,4 @@ | |||||
<?xml version="1.0" encoding="utf-8"?> | |||||
<packages> | |||||
<package id="zlib.net" version="1.0.4.0" targetFramework="net46" /> | |||||
</packages> |
@ -1,190 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Collections.Concurrent; | |||||
using System.IO; | |||||
using System.Runtime.Remoting.Messaging; | |||||
using System.Threading; | |||||
using IllusionPlugin; | |||||
namespace IllusionPlugin { | |||||
/// <summary> | |||||
/// A general purpose logging class for any plugin to use. | |||||
/// </summary> | |||||
public class Logger { | |||||
private static BlockingCollection<logMessage> _logQueue; | |||||
private static Thread _watcherThread; | |||||
private static bool _threadRunning; | |||||
private readonly FileInfo _logFile; | |||||
private string ModName; | |||||
struct logMessage { | |||||
public static readonly string logFormat = "[{3} @ {2:HH:mm:ss} | {1}] {0}"; | |||||
public WarningLevel WarningLevel; | |||||
public DateTime Time; | |||||
public Logger Log; | |||||
public string Message; | |||||
public logMessage(string msg, Logger log, DateTime time, WarningLevel wl) { | |||||
Message = msg; | |||||
WarningLevel = wl; | |||||
Log = log; | |||||
Time = time; | |||||
} | |||||
} | |||||
enum WarningLevel { | |||||
Log, Error, Exception, Warning | |||||
} | |||||
static void SetupStatic() | |||||
{ | |||||
if (_logQueue == null) | |||||
_logQueue = new BlockingCollection<logMessage>(); | |||||
if (_watcherThread == null || !_watcherThread.IsAlive) | |||||
{ | |||||
_watcherThread = new Thread(QueueWatcher); // { IsBackground = true }; | |||||
_threadRunning = true; | |||||
_watcherThread.Start(); | |||||
} | |||||
} | |||||
/// <summary> | |||||
/// Creates a logger with the specified name. | |||||
/// </summary> | |||||
/// <param name="modName">the name of the logger</param> | |||||
public Logger(string modName = "Default") { | |||||
SetupStatic(); | |||||
_logFile = GetPath(modName); | |||||
_logFile.Create().Close(); | |||||
} | |||||
/// <summary> | |||||
/// Creates a logger for the specified plugin. | |||||
/// </summary> | |||||
/// <param name="plugin">the plugin to associate the logger with</param> | |||||
public Logger(IBeatSaberPlugin plugin) | |||||
{ | |||||
SetupStatic(); | |||||
_logFile = GetPath(plugin); | |||||
_logFile.Create().Close(); | |||||
} | |||||
/// <summary> | |||||
/// Sends a message to the log. | |||||
/// </summary> | |||||
/// <param name="msg">the message to send</param> | |||||
public void Log(string msg) { | |||||
if(!_watcherThread.IsAlive) throw new Exception("Logger is Closed!"); | |||||
//_logQueue.Add(new logMessage($"[LOG @ {DateTime.Now:HH:mm:ss} | {ModName}] {msg}", WarningLevel.Log)); | |||||
_logQueue.Add(new logMessage(msg, this, DateTime.Now, WarningLevel.Log)); | |||||
} | |||||
/// <summary> | |||||
/// Sends an error to the log. | |||||
/// </summary> | |||||
/// <param name="msg">the message to send</param> | |||||
public void Error(string msg) { | |||||
if(!_watcherThread.IsAlive) throw new Exception("Logger is Closed!"); | |||||
//_logQueue.Add(new logMessage($"[ERROR @ {DateTime.Now:HH:mm:ss} | {ModName}] {msg}", WarningLevel.Error)); | |||||
_logQueue.Add(new logMessage(msg, this, DateTime.Now, WarningLevel.Error)); | |||||
} | |||||
/// <summary> | |||||
/// Sends an exception to the log. | |||||
/// </summary> | |||||
/// <param name="msg">the message to send</param> | |||||
public void Exception(string msg) { | |||||
if(!_watcherThread.IsAlive) throw new Exception("Logger is Closed!"); | |||||
//_logQueue.Add(new logMessage($"[EXCEPTION @ {DateTime.Now:HH:mm:ss} | {ModName}] {msg}", WarningLevel.Exception)); | |||||
_logQueue.Add(new logMessage(msg, this, DateTime.Now, WarningLevel.Exception)); | |||||
} | |||||
/// <summary> | |||||
/// Sends a warning to the log. | |||||
/// </summary> | |||||
/// <param name="msg">the message to send</param> | |||||
public void Warning(string msg) { | |||||
if(!_watcherThread.IsAlive) throw new Exception("Logger is Closed!"); | |||||
//_logQueue.Add(new logMessage($"[WARNING @ {DateTime.Now:HH:mm:ss} | {ModName}] {msg}", WarningLevel.Warning)); | |||||
_logQueue.Add(new logMessage(msg, this, DateTime.Now, WarningLevel.Warning)); | |||||
} | |||||
static void QueueWatcher() { | |||||
//StreamWriter wstream = null; | |||||
Dictionary<string, StreamWriter> wstreams = new Dictionary<string, StreamWriter>(); | |||||
while (_threadRunning && _logQueue.TryTake(out logMessage message, Timeout.Infinite)) | |||||
{ | |||||
string msg = string.Format(logMessage.logFormat, message.Message, message.Log.ModName, message.Time, message.WarningLevel); | |||||
wstreams[message.Log.ModName] = message.Log._logFile.AppendText(); | |||||
wstreams[message.Log.ModName].WriteLine(msg); | |||||
Console.ForegroundColor = GetConsoleColour(message.WarningLevel); | |||||
Console.WriteLine(message.Message); | |||||
Console.ResetColor(); | |||||
if (_logQueue.Count == 0) | |||||
{ // no more messages | |||||
foreach (var kvp in wstreams) | |||||
{ | |||||
if (kvp.Value == null) continue; | |||||
kvp.Value.Dispose(); | |||||
wstreams[kvp.Key] = null; | |||||
} | |||||
} | |||||
} | |||||
foreach (var kvp in wstreams) | |||||
{ | |||||
if (kvp.Value == null) continue; | |||||
kvp.Value.Dispose(); | |||||
} | |||||
} | |||||
/// <summary> | |||||
/// Stops the logger background thread. | |||||
/// </summary> | |||||
public static void Stop() { | |||||
_threadRunning = false; | |||||
_watcherThread.Join(); | |||||
} | |||||
static ConsoleColor GetConsoleColour(WarningLevel level) { | |||||
switch (level) { | |||||
case WarningLevel.Log: | |||||
return ConsoleColor.Green; | |||||
case WarningLevel.Error: | |||||
return ConsoleColor.Yellow; | |||||
case WarningLevel.Exception: | |||||
return ConsoleColor.Red; | |||||
case WarningLevel.Warning: | |||||
return ConsoleColor.Blue; | |||||
default: | |||||
return ConsoleColor.Gray; | |||||
} | |||||
} | |||||
FileInfo GetPath(IBeatSaberPlugin plugin) => GetPath(plugin.Name); | |||||
FileInfo GetPath(string modName) { | |||||
ModName = modName; | |||||
var logsDir = new DirectoryInfo($"./Logs/{modName}/{DateTime.Now:dd-MM-yy}"); | |||||
logsDir.Create(); | |||||
return new FileInfo($"{logsDir.FullName}/{logsDir.GetFiles().Length}.txt"); | |||||
} | |||||
} | |||||
/// <summary> | |||||
/// | |||||
/// </summary> | |||||
public static class LoggerExtensions { | |||||
/// <summary> | |||||
/// Gets a logger for the provided plugin. | |||||
/// </summary> | |||||
/// <param name="plugin">the plugin to get a logger for</param> | |||||
/// <returns>a Logger instance</returns> | |||||
public static Logger GetLogger(this IBeatSaberPlugin plugin) { | |||||
return new Logger(plugin); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,16 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace IllusionPlugin.Logging | |||||
{ | |||||
public abstract class LogPrinter | |||||
{ | |||||
public abstract Logger.LogLevel Filter { get; set; } | |||||
public abstract void Print(Logger.Level level, DateTime time, string logName, string message); | |||||
public virtual void StartPrint() { } | |||||
public virtual void EndPrint() { } | |||||
} | |||||
} |
@ -0,0 +1,52 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace IllusionPlugin.Logging | |||||
{ | |||||
public abstract class Logger | |||||
{ | |||||
public static string LogFormat { get; protected internal set; } = "[{3} @ {2:HH:mm:ss} | {1}] {0}"; | |||||
public enum Level : byte | |||||
{ | |||||
None = 0, | |||||
Debug = 1, | |||||
Info = 2, | |||||
Warning = 4, | |||||
Error = 8, | |||||
Critical = 16 | |||||
} | |||||
[Flags] | |||||
public enum LogLevel : byte | |||||
{ | |||||
None = Level.None, | |||||
DebugOnly = Level.Debug, | |||||
InfoOnly = Level.Info, | |||||
WarningOnly = Level.Warning, | |||||
ErrorOnly = Level.Error, | |||||
CriticalOnly = Level.Critical, | |||||
ErrorUp = ErrorOnly | CriticalOnly, | |||||
WarningUp = WarningOnly | ErrorUp, | |||||
InfoUp = InfoOnly | WarningUp, | |||||
All = DebugOnly | InfoUp, | |||||
} | |||||
public abstract void Log(Level level, string message); | |||||
public void Log(Level level, Exception exeption) => Log(level, exeption.ToString()); | |||||
public void Debug(string message) => Log(Level.Debug, message); | |||||
public void Debug(Exception e) => Log(Level.Debug, e); | |||||
public void Info(string message) => Log(Level.Info, message); | |||||
public void Info(Exception e) => Log(Level.Info, e); | |||||
public void Warn(string message) => Log(Level.Warning, message); | |||||
public void Warn(Exception e) => Log(Level.Warning, e); | |||||
public void Error(string message) => Log(Level.Error, message); | |||||
public void Error(Exception e) => Log(Level.Error, e); | |||||
public void Critical(string message) => Log(Level.Critical, message); | |||||
public void Critical(Exception e) => Log(Level.Critical, e); | |||||
} | |||||
} |
@ -1 +1 @@ | |||||
eb108721e2940659deee6bd068631e6d4749736e | |||||
1bbd9ba53463d8e0c57caf8203d0145bfc70fa26 |