You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

189 lines
7.0 KiB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.Concurrent;
  4. using System.IO;
  5. using System.Runtime.Remoting.Messaging;
  6. using System.Threading;
  7. using IllusionPlugin;
  8. namespace IllusionPlugin {
  9. /// <summary>
  10. /// A general purpose logging class for any plugin to use.
  11. /// </summary>
  12. public class Logger {
  13. private static BlockingCollection<logMessage> _logQueue;
  14. private static Thread _watcherThread;
  15. private static bool _threadRunning;
  16. private readonly FileInfo _logFile;
  17. private string ModName;
  18. struct logMessage {
  19. public static readonly string logFormat = "[{3} @ {2:HH:mm:ss} | {1}] {0}";
  20. public WarningLevel WarningLevel;
  21. public DateTime Time;
  22. public Logger Log;
  23. public string Message;
  24. public logMessage(string msg, Logger log, DateTime time, WarningLevel wl) {
  25. Message = msg;
  26. WarningLevel = wl;
  27. Log = log;
  28. Time = time;
  29. }
  30. }
  31. enum WarningLevel {
  32. Log, Error, Exception, Warning
  33. }
  34. static void SetupStatic()
  35. {
  36. if (_logQueue == null)
  37. _logQueue = new BlockingCollection<logMessage>();
  38. if (_watcherThread == null || !_watcherThread.IsAlive)
  39. {
  40. _watcherThread = new Thread(QueueWatcher); // { IsBackground = true };
  41. _threadRunning = true;
  42. _watcherThread.Start();
  43. }
  44. }
  45. /// <summary>
  46. /// Creates a logger with the specified name.
  47. /// </summary>
  48. /// <param name="modName">the name of the logger</param>
  49. public Logger(string modName = "Default") {
  50. SetupStatic();
  51. _logFile = GetPath(modName);
  52. _logFile.Create().Close();
  53. }
  54. /// <summary>
  55. /// Creates a logger for the specified plugin.
  56. /// </summary>
  57. /// <param name="plugin">the plugin to associate the logger with</param>
  58. public Logger(IBeatSaberPlugin plugin)
  59. {
  60. SetupStatic();
  61. _logFile = GetPath(plugin);
  62. _logFile.Create().Close();
  63. }
  64. /// <summary>
  65. /// Sends a message to the log.
  66. /// </summary>
  67. /// <param name="msg">the message to send</param>
  68. public void Log(string msg) {
  69. if(!_watcherThread.IsAlive) throw new Exception("Logger is Closed!");
  70. //_logQueue.Add(new logMessage($"[LOG @ {DateTime.Now:HH:mm:ss} | {ModName}] {msg}", WarningLevel.Log));
  71. _logQueue.Add(new logMessage(msg, this, DateTime.Now, WarningLevel.Log));
  72. }
  73. /// <summary>
  74. /// Sends an error to the log.
  75. /// </summary>
  76. /// <param name="msg">the message to send</param>
  77. public void Error(string msg) {
  78. if(!_watcherThread.IsAlive) throw new Exception("Logger is Closed!");
  79. //_logQueue.Add(new logMessage($"[ERROR @ {DateTime.Now:HH:mm:ss} | {ModName}] {msg}", WarningLevel.Error));
  80. _logQueue.Add(new logMessage(msg, this, DateTime.Now, WarningLevel.Error));
  81. }
  82. /// <summary>
  83. /// Sends an exception to the log.
  84. /// </summary>
  85. /// <param name="msg">the message to send</param>
  86. public void Exception(string msg) {
  87. if(!_watcherThread.IsAlive) throw new Exception("Logger is Closed!");
  88. //_logQueue.Add(new logMessage($"[EXCEPTION @ {DateTime.Now:HH:mm:ss} | {ModName}] {msg}", WarningLevel.Exception));
  89. _logQueue.Add(new logMessage(msg, this, DateTime.Now, WarningLevel.Exception));
  90. }
  91. /// <summary>
  92. /// Sends a warning to the log.
  93. /// </summary>
  94. /// <param name="msg">the message to send</param>
  95. public void Warning(string msg) {
  96. if(!_watcherThread.IsAlive) throw new Exception("Logger is Closed!");
  97. //_logQueue.Add(new logMessage($"[WARNING @ {DateTime.Now:HH:mm:ss} | {ModName}] {msg}", WarningLevel.Warning));
  98. _logQueue.Add(new logMessage(msg, this, DateTime.Now, WarningLevel.Warning));
  99. }
  100. static void QueueWatcher() {
  101. //StreamWriter wstream = null;
  102. Dictionary<string, StreamWriter> wstreams = new Dictionary<string, StreamWriter>();
  103. while (_threadRunning && _logQueue.TryTake(out logMessage message, Timeout.Infinite))
  104. {
  105. string msg = string.Format(logMessage.logFormat, message.Message, message.Log.ModName, message.Time, message.WarningLevel);
  106. wstreams[message.Log.ModName] = message.Log._logFile.AppendText();
  107. wstreams[message.Log.ModName].WriteLine(msg);
  108. Console.ForegroundColor = GetConsoleColour(message.WarningLevel);
  109. Console.WriteLine(message.Message);
  110. Console.ResetColor();
  111. if (_logQueue.Count == 0)
  112. { // no more messages
  113. foreach (var kvp in wstreams)
  114. {
  115. if (kvp.Value == null) continue;
  116. kvp.Value.Dispose();
  117. wstreams[kvp.Key] = null;
  118. }
  119. }
  120. }
  121. foreach (var kvp in wstreams)
  122. {
  123. if (kvp.Value == null) continue;
  124. kvp.Value.Dispose();
  125. }
  126. }
  127. /// <summary>
  128. /// Stops the logger background thread.
  129. /// </summary>
  130. public static void Stop() {
  131. _threadRunning = false;
  132. _watcherThread.Join();
  133. }
  134. static ConsoleColor GetConsoleColour(WarningLevel level) {
  135. switch (level) {
  136. case WarningLevel.Log:
  137. return ConsoleColor.Green;
  138. case WarningLevel.Error:
  139. return ConsoleColor.Yellow;
  140. case WarningLevel.Exception:
  141. return ConsoleColor.Red;
  142. case WarningLevel.Warning:
  143. return ConsoleColor.Blue;
  144. default:
  145. return ConsoleColor.Gray;
  146. }
  147. }
  148. FileInfo GetPath(IBeatSaberPlugin plugin) => GetPath(plugin.Name);
  149. FileInfo GetPath(string modName) {
  150. ModName = modName;
  151. var logsDir = new DirectoryInfo($"./Logs/{modName}/{DateTime.Now:dd-MM-yy}");
  152. logsDir.Create();
  153. return new FileInfo($"{logsDir.FullName}/{logsDir.GetFiles().Length}.txt");
  154. }
  155. }
  156. /// <summary>
  157. ///
  158. /// </summary>
  159. public static class LoggerExtensions {
  160. /// <summary>
  161. /// Gets a logger for the provided plugin.
  162. /// </summary>
  163. /// <param name="plugin">the plugin to get a logger for</param>
  164. /// <returns>a Logger instance</returns>
  165. public static Logger GetLogger(this IBeatSaberPlugin plugin) {
  166. return new Logger(plugin);
  167. }
  168. }
  169. }