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.

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