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.

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