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.

220 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; } = 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. if (ModPrefs.GetBool("IPA", "PrintDebug", false, true))
  59. PrintFilter = LogLevel.All;
  60. showSourceClass = ModPrefs.GetBool("IPA", "DebugShowCallSource", false, true);
  61. }
  62. private StandardLogger(string mainName, string subName, params LogPrinter[] inherited)
  63. {
  64. logName = $"{mainName}/{subName}";
  65. printers = new List<LogPrinter>(inherited)
  66. {
  67. new PluginSubLogPrinter(mainName, subName)
  68. };
  69. if (_logThread == null || !_logThread.IsAlive)
  70. {
  71. _logThread = new Thread(LogThread);
  72. _logThread.Start();
  73. }
  74. }
  75. internal StandardLogger(string name)
  76. {
  77. logName = name;
  78. printers.Add(new PluginLogFilePrinter(name));
  79. if (_logThread == null || !_logThread.IsAlive)
  80. {
  81. _logThread = new Thread(LogThread);
  82. _logThread.Start();
  83. }
  84. }
  85. internal StandardLogger GetChild(string name)
  86. {
  87. if (!children.TryGetValue(name, out StandardLogger chld))
  88. {
  89. chld = new StandardLogger(logName, name, printers.ToArray());
  90. children.Add(name, chld);
  91. }
  92. return chld;
  93. }
  94. /// <summary>
  95. /// Logs a specific message at a given level.
  96. /// </summary>
  97. /// <param name="level">the message level</param>
  98. /// <param name="message">the message to log</param>
  99. public override void Log(Level level, string message)
  100. {
  101. _logQueue.Add(new LogMessage
  102. {
  103. level = level,
  104. message = message,
  105. logger = this,
  106. time = DateTime.Now
  107. });
  108. }
  109. /// <summary>
  110. /// An override to <see cref="Logger.Debug(string)"/> which shows the method that called it.
  111. /// </summary>
  112. /// <param name="message">the message to log</param>
  113. public override void Debug(string message)
  114. { // add source to message
  115. var stfm = new StackTrace().GetFrame(1).GetMethod();
  116. if (showSourceClass)
  117. base.Debug($"{{{stfm.DeclaringType.FullName}::{stfm.Name}}} {message}");
  118. else
  119. base.Debug(message);
  120. }
  121. internal struct LogMessage
  122. {
  123. public Level level;
  124. public StandardLogger logger;
  125. public string message;
  126. public DateTime time;
  127. }
  128. private static BlockingCollection<LogMessage> _logQueue = new BlockingCollection<LogMessage>();
  129. private static Thread _logThread;
  130. private static void LogThread()
  131. {
  132. HashSet<LogPrinter> started = new HashSet<LogPrinter>();
  133. while (_logQueue.TryTake(out LogMessage msg, Timeout.Infinite)) {
  134. foreach (var printer in msg.logger.printers)
  135. {
  136. try
  137. {
  138. if (((byte)msg.level & (byte)printer.Filter) != 0)
  139. {
  140. if (!started.Contains(printer))
  141. {
  142. printer.StartPrint();
  143. started.Add(printer);
  144. }
  145. printer.Print(msg.level, msg.time, msg.logger.logName, msg.message);
  146. }
  147. }
  148. catch (Exception e)
  149. {
  150. Console.WriteLine($"printer errored {e}");
  151. }
  152. }
  153. if (_logQueue.Count == 0)
  154. {
  155. foreach (var printer in started)
  156. {
  157. try
  158. {
  159. printer.EndPrint();
  160. }
  161. catch (Exception e)
  162. {
  163. Console.WriteLine($"printer errored {e}");
  164. }
  165. }
  166. started.Clear();
  167. }
  168. }
  169. }
  170. internal static void StopLogThread()
  171. {
  172. _logQueue.CompleteAdding();
  173. _logThread.Join();
  174. }
  175. }
  176. /// <summary>
  177. /// A class providing extensions for various loggers.
  178. /// </summary>
  179. public static class LoggerExtensions
  180. {
  181. /// <summary>
  182. /// Gets a child logger, if supported.
  183. /// </summary>
  184. /// <param name="logger">the parent <see cref="Logger"/></param>
  185. /// <param name="name">the name of the child</param>
  186. /// <returns>the child logger</returns>
  187. public static Logger GetChildLogger(this Logger logger, string name)
  188. {
  189. if (logger is StandardLogger)
  190. {
  191. return (logger as StandardLogger).GetChild(name);
  192. }
  193. else
  194. {
  195. throw new InvalidOperationException();
  196. }
  197. }
  198. }
  199. }