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.

229 lines
7.2 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 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; }
  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. PrintFilter = ModPrefs.GetBool("IPA", "PrintDebug", false, true) ? LogLevel.All : LogLevel.InfoUp;
  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. /// Adds a log printer to the logger.
  96. /// </summary>
  97. /// <param name="printer">the printer to add</param>
  98. public void AddPrinter(LogPrinter printer)
  99. {
  100. printers.Add(printer);
  101. }
  102. /// <summary>
  103. /// Logs a specific message at a given level.
  104. /// </summary>
  105. /// <param name="level">the message level</param>
  106. /// <param name="message">the message to log</param>
  107. public override void Log(Level level, string message)
  108. {
  109. _logQueue.Add(new LogMessage
  110. {
  111. level = level,
  112. message = message,
  113. logger = this,
  114. time = DateTime.Now
  115. });
  116. }
  117. /// <summary>
  118. /// An override to <see cref="Logger.Debug(string)"/> which shows the method that called it.
  119. /// </summary>
  120. /// <param name="message">the message to log</param>
  121. public override void Debug(string message)
  122. { // add source to message
  123. var stfm = new StackTrace().GetFrame(1).GetMethod();
  124. if (showSourceClass)
  125. base.Debug($"{{{stfm.DeclaringType.FullName}::{stfm.Name}}} {message}");
  126. else
  127. base.Debug(message);
  128. }
  129. internal struct LogMessage
  130. {
  131. public Level level;
  132. public StandardLogger logger;
  133. public string message;
  134. public DateTime time;
  135. }
  136. private static BlockingCollection<LogMessage> _logQueue = new BlockingCollection<LogMessage>();
  137. private static Thread _logThread;
  138. private static void LogThread()
  139. {
  140. HashSet<LogPrinter> started = new HashSet<LogPrinter>();
  141. while (_logQueue.TryTake(out LogMessage msg, Timeout.Infinite)) {
  142. foreach (var printer in msg.logger.printers)
  143. {
  144. try
  145. {
  146. if (((byte)msg.level & (byte)printer.Filter) != 0)
  147. {
  148. if (!started.Contains(printer))
  149. {
  150. printer.StartPrint();
  151. started.Add(printer);
  152. }
  153. printer.Print(msg.level, msg.time, msg.logger.logName, msg.message);
  154. }
  155. }
  156. catch (Exception e)
  157. {
  158. Console.WriteLine($"printer errored {e}");
  159. }
  160. }
  161. if (_logQueue.Count == 0)
  162. {
  163. foreach (var printer in started)
  164. {
  165. try
  166. {
  167. printer.EndPrint();
  168. }
  169. catch (Exception e)
  170. {
  171. Console.WriteLine($"printer errored {e}");
  172. }
  173. }
  174. started.Clear();
  175. }
  176. }
  177. }
  178. internal static void StopLogThread()
  179. {
  180. _logQueue.CompleteAdding();
  181. _logThread.Join();
  182. }
  183. }
  184. /// <summary>
  185. /// A class providing extensions for various loggers.
  186. /// </summary>
  187. public static class LoggerExtensions
  188. {
  189. /// <summary>
  190. /// Gets a child logger, if supported.
  191. /// </summary>
  192. /// <param name="logger">the parent <see cref="Logger"/></param>
  193. /// <param name="name">the name of the child</param>
  194. /// <returns>the child logger</returns>
  195. public static Logger GetChildLogger(this Logger logger, string name)
  196. {
  197. if (logger is StandardLogger)
  198. {
  199. return (logger as StandardLogger).GetChild(name);
  200. }
  201. else
  202. {
  203. throw new InvalidOperationException();
  204. }
  205. }
  206. }
  207. }