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
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; }
  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. /// 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. }