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.

218 lines
6.9 KiB

6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
  1. using IPA.Logging;
  2. using System;
  3. using System.Collections.Concurrent;
  4. using System.Collections.Generic;
  5. using System.Diagnostics;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Threading;
  9. using System.Threading.Tasks;
  10. using IPA;
  11. using IPA.Logging.Printers;
  12. namespace IPA.Logging
  13. {
  14. /// <summary>
  15. /// The default <see cref="Logger"/> implimentation.
  16. /// </summary>
  17. public class StandardLogger : Logger
  18. {
  19. private static readonly IReadOnlyList<LogPrinter> defaultPrinters = new List<LogPrinter>()
  20. {
  21. new ColoredConsolePrinter()
  22. {
  23. Filter = LogLevel.DebugOnly,
  24. Color = ConsoleColor.Green,
  25. },
  26. new ColoredConsolePrinter()
  27. {
  28. Filter = LogLevel.InfoOnly,
  29. Color = ConsoleColor.White,
  30. },
  31. new ColoredConsolePrinter()
  32. {
  33. Filter = LogLevel.WarningOnly,
  34. Color = ConsoleColor.Yellow,
  35. },
  36. new ColoredConsolePrinter()
  37. {
  38. Filter = LogLevel.ErrorOnly,
  39. Color = ConsoleColor.Red,
  40. },
  41. new ColoredConsolePrinter()
  42. {
  43. Filter = LogLevel.CriticalOnly,
  44. Color = ConsoleColor.Magenta,
  45. },
  46. new GlobalLogFilePrinter()
  47. };
  48. private string logName;
  49. private static readonly bool showSourceClass = true;
  50. /// <summary>
  51. /// All levels defined by this filter will be sent to loggers. All others will be ignored.
  52. /// </summary>
  53. public static LogLevel PrintFilter { get; set; } = ModPrefs.GetBool("IPA", "PrintDebug", false, true) ? LogLevel.All : LogLevel.InfoUp;
  54. private List<LogPrinter> printers = new List<LogPrinter>(defaultPrinters);
  55. private Dictionary<string, StandardLogger> children = new Dictionary<string, StandardLogger>();
  56. static StandardLogger()
  57. {
  58. showSourceClass = ModPrefs.GetBool("IPA", "DebugShowCallSource", false, true);
  59. }
  60. private StandardLogger(string mainName, string subName, params LogPrinter[] inherited)
  61. {
  62. logName = $"{mainName}/{subName}";
  63. printers = new List<LogPrinter>(inherited)
  64. {
  65. new PluginSubLogPrinter(mainName, subName)
  66. };
  67. if (_logThread == null || !_logThread.IsAlive)
  68. {
  69. _logThread = new Thread(LogThread);
  70. _logThread.Start();
  71. }
  72. }
  73. internal StandardLogger(string name)
  74. {
  75. logName = name;
  76. printers.Add(new PluginLogFilePrinter(name));
  77. if (_logThread == null || !_logThread.IsAlive)
  78. {
  79. _logThread = new Thread(LogThread);
  80. _logThread.Start();
  81. }
  82. }
  83. internal StandardLogger GetChild(string name)
  84. {
  85. if (!children.TryGetValue(name, out StandardLogger chld))
  86. {
  87. chld = new StandardLogger(logName, name, printers.ToArray());
  88. children.Add(name, chld);
  89. }
  90. return chld;
  91. }
  92. /// <summary>
  93. /// Logs a specific message at a given level.
  94. /// </summary>
  95. /// <param name="level">the message level</param>
  96. /// <param name="message">the message to log</param>
  97. public override void Log(Level level, string message)
  98. {
  99. _logQueue.Add(new LogMessage
  100. {
  101. level = level,
  102. message = message,
  103. logger = this,
  104. time = DateTime.Now
  105. });
  106. }
  107. /// <summary>
  108. /// An override to <see cref="Logger.Debug(string)"/> which shows the method that called it.
  109. /// </summary>
  110. /// <param name="message">the message to log</param>
  111. public override void Debug(string message)
  112. { // add source to message
  113. var stfm = new StackTrace().GetFrame(1).GetMethod();
  114. if (showSourceClass)
  115. base.Debug($"{{{stfm.DeclaringType.FullName}::{stfm.Name}}} {message}");
  116. else
  117. base.Debug(message);
  118. }
  119. internal struct LogMessage
  120. {
  121. public Level level;
  122. public StandardLogger logger;
  123. public string message;
  124. public DateTime time;
  125. }
  126. private static BlockingCollection<LogMessage> _logQueue = new BlockingCollection<LogMessage>();
  127. private static Thread _logThread;
  128. private static void LogThread()
  129. {
  130. HashSet<LogPrinter> started = new HashSet<LogPrinter>();
  131. while (_logQueue.TryTake(out LogMessage msg, Timeout.Infinite)) {
  132. foreach (var printer in msg.logger.printers)
  133. {
  134. try
  135. {
  136. if (((byte)msg.level & (byte)printer.Filter) != 0)
  137. {
  138. if (!started.Contains(printer))
  139. {
  140. printer.StartPrint();
  141. started.Add(printer);
  142. }
  143. printer.Print(msg.level, msg.time, msg.logger.logName, msg.message);
  144. }
  145. }
  146. catch (Exception e)
  147. {
  148. Console.WriteLine($"printer errored {e}");
  149. }
  150. }
  151. if (_logQueue.Count == 0)
  152. {
  153. foreach (var printer in started)
  154. {
  155. try
  156. {
  157. printer.EndPrint();
  158. }
  159. catch (Exception e)
  160. {
  161. Console.WriteLine($"printer errored {e}");
  162. }
  163. }
  164. started.Clear();
  165. }
  166. }
  167. }
  168. internal static void StopLogThread()
  169. {
  170. _logQueue.CompleteAdding();
  171. _logThread.Join();
  172. }
  173. }
  174. /// <summary>
  175. /// A class providing extensions for various loggers.
  176. /// </summary>
  177. public static class LoggerExtensions
  178. {
  179. /// <summary>
  180. /// Gets a child logger, if supported.
  181. /// </summary>
  182. /// <param name="logger">the parent <see cref="Logger"/></param>
  183. /// <param name="name">the name of the child</param>
  184. /// <returns>the child logger</returns>
  185. public static Logger GetChildLogger(this Logger logger, string name)
  186. {
  187. if (logger is StandardLogger)
  188. {
  189. return (logger as StandardLogger).GetChild(name);
  190. }
  191. else
  192. {
  193. throw new InvalidOperationException();
  194. }
  195. }
  196. }
  197. }