DebugLogger.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. using Microsoft.Extensions.Logging;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Diagnostics.CodeAnalysis;
  5. namespace Wayne.Lib.Log
  6. {
  7. /// <summary>
  8. /// Class used to make debug logs.
  9. /// </summary>
  10. /// <example>
  11. /// This is an example of how to write a debug log entry.
  12. /// <code>
  13. /// using (DebugLogger dLog = new DebugLogger(this))
  14. /// {
  15. /// if (dLog.IsActive(DebugLogLevel.Detailed))
  16. /// {
  17. /// dLog.Add("This is line 1.", DebugLogLevel.Detailed);
  18. /// dLog.Add("This is line 2.", "MyCategory", DebugLogLevel.Detailed);
  19. /// }
  20. /// }
  21. /// </code>
  22. /// </example>
  23. public sealed class DebugLogger : IDisposable, IIdentifiableEntity, IDebugLogger
  24. {
  25. public static ILogger logger { get; set; }
  26. #region Inner class
  27. class CategoryDebugLevel
  28. {
  29. public object Category { get; set; }
  30. public DebugLogLevel LogLevel { get; set; }
  31. }
  32. #endregion
  33. #region Fields
  34. private IIdentifiableEntity entity;
  35. private bool persistent;
  36. private object categoryDebugLevelLock = new object();
  37. private Dictionary<object, DebugLogLevel> categoryDebugLevel = new Dictionary<object, DebugLogLevel>();
  38. private bool logPersistentInfo;
  39. private bool disposed;
  40. private DebugLogLevel? defaultCategoryDebugLogLevel;
  41. private CategoryDebugLevel latestCategoryDebugLogLevel;
  42. #endregion
  43. #region Constructors
  44. /// <summary>
  45. /// Construction of non-persistent DebugLogger.
  46. /// </summary>
  47. /// <param name="entity"></param>
  48. public DebugLogger(IIdentifiableEntity entity)
  49. {
  50. //if (Logger.IsClosed)
  51. // disposed = true;// throw new LogException(LogExceptionType.LoggerClosed);
  52. if (entity != null)
  53. this.entity = entity;
  54. else
  55. this.entity = IdentifiableEntity.Empty;
  56. }
  57. /// <summary>
  58. /// Construction
  59. /// </summary>
  60. /// <param name="entity"></param>
  61. /// <param name="persistent"></param>
  62. public DebugLogger(IIdentifiableEntity entity, bool persistent)
  63. {
  64. //if (Logger.IsClosed)
  65. // disposed = true;// throw new LogException(LogExceptionType.LoggerClosed);
  66. //else
  67. {
  68. if (entity != null)
  69. this.entity = entity;
  70. else
  71. this.entity = IdentifiableEntity.Empty;
  72. this.persistent = persistent;
  73. if (persistent)
  74. {
  75. //Logger.RegisterPersistentLogObject(this as DebugLogger);
  76. logPersistentInfo = true;
  77. }
  78. }
  79. }
  80. /// <summary>
  81. /// Finalizer.
  82. /// </summary>
  83. ~DebugLogger()
  84. {
  85. Dispose(false);
  86. }
  87. #endregion
  88. #region IDisposable Members
  89. /// <summary>
  90. /// Dispose.
  91. /// </summary>
  92. public void Dispose()
  93. {
  94. Dispose(true);
  95. GC.SuppressFinalize(this);
  96. }
  97. /// <summary>
  98. /// Dispose.
  99. /// </summary>
  100. private void Dispose(bool disposing)
  101. {
  102. if (!disposed)
  103. {
  104. disposed = true;
  105. if (disposing)
  106. {
  107. //if (persistent)
  108. //{
  109. // Logger.UnregisterPersistentLogObject(this as DebugLogger);
  110. //}
  111. //Logger.UnregisterEntity(entity);
  112. }
  113. lock (categoryDebugLevelLock)
  114. {
  115. categoryDebugLevel.Clear();
  116. }
  117. }
  118. }
  119. #endregion
  120. #region Properties
  121. /// <summary>
  122. /// The identifiable entity that has created this debug log.
  123. /// </summary>
  124. public IIdentifiableEntity Entity
  125. {
  126. get { return entity; }
  127. }
  128. /// <summary>
  129. /// Tells whether the debug log is persistent or not.
  130. /// </summary>
  131. public bool Persistent
  132. {
  133. get { return persistent; }
  134. }
  135. #endregion
  136. #region Methods: IsActive
  137. /// <summary>
  138. /// Tells whether the default category is active in the Normal level.
  139. /// </summary>
  140. public bool IsActive()
  141. {
  142. //return !disposed && (DebugLogLevel.Normal <= GetDebugLevel(string.Empty));
  143. return true;
  144. }
  145. /// <summary>
  146. /// Tells whether the given category is active in the Normal level.
  147. /// </summary>
  148. public bool IsActive(object category)
  149. {
  150. return !disposed && (DebugLogLevel.Normal <= GetDebugLevel(category));
  151. }
  152. /// <summary>
  153. /// Tells whether the default category is active in the given level.
  154. /// </summary>
  155. public bool IsActive(DebugLogLevel debugLogLevel)
  156. {
  157. return !disposed && (debugLogLevel != DebugLogLevel.Excluded) && (debugLogLevel <= GetDebugLevel(string.Empty));
  158. }
  159. /// <summary>
  160. /// Tells whether the given category is active in the given level.
  161. /// </summary>
  162. public bool IsActive(object category, DebugLogLevel debugLogLevel)
  163. {
  164. return !disposed && (debugLogLevel != DebugLogLevel.Excluded) && (debugLogLevel <= GetDebugLevel(category));
  165. }
  166. #endregion
  167. #region Methods: GetDebugLevel
  168. /// <summary>
  169. /// Get the current debug level for the default category.
  170. /// </summary>
  171. public DebugLogLevel GetDebugLevel()
  172. {
  173. return GetDebugLevel(string.Empty);
  174. }
  175. /// <summary>
  176. /// Get the current debug level for the given category.
  177. /// </summary>
  178. public DebugLogLevel GetDebugLevel(object category)
  179. {
  180. //if (disposed || Logger.IsClosed)
  181. //{
  182. // return DebugLogLevel.Excluded;
  183. //}
  184. if (category == null)
  185. {
  186. category = string.Empty;
  187. }
  188. //Cached empty category
  189. if (category.Equals(string.Empty))
  190. {
  191. if (defaultCategoryDebugLogLevel.HasValue)
  192. return defaultCategoryDebugLogLevel.Value;
  193. }
  194. //Cached latest category
  195. var localCachedItem = latestCategoryDebugLogLevel;
  196. if (localCachedItem != null && localCachedItem.Category.Equals(category))
  197. {
  198. return localCachedItem.LogLevel;
  199. }
  200. // Cached Dictionary lookup of category's debuglevel
  201. lock (categoryDebugLevelLock)
  202. {
  203. DebugLogLevel cachedDebugLogLevel;
  204. if (categoryDebugLevel.TryGetValue(category, out cachedDebugLogLevel))
  205. {
  206. latestCategoryDebugLogLevel = new CategoryDebugLevel()
  207. {
  208. Category = category,
  209. LogLevel = cachedDebugLogLevel
  210. };
  211. return cachedDebugLogLevel;
  212. }
  213. }
  214. //Not cached - evaluate and populate cache!
  215. // Get a list of the logWriters that wants to log me.
  216. //EntityCategory entityCategory = Logger.DebugConfig.GetEntityCategory(entity, category);
  217. //LogWriter[] logWriters = Logger.DebugConfig.GetLogWriters(entityCategory);
  218. // Iterate through the writers and get the highest debug level.
  219. DebugLogLevel debugLogLevel = DebugLogLevel.Excluded;
  220. //foreach (LogWriter writer in logWriters)
  221. //{
  222. // debugLogLevel = MaxDebugLogLevel(debugLogLevel, writer.GetDebugLogLevel(entityCategory));
  223. //}
  224. //Populate Cache for default category
  225. if (category.Equals(string.Empty))
  226. {
  227. defaultCategoryDebugLogLevel = debugLogLevel;
  228. }
  229. else
  230. {
  231. //Populate Cache for latest category
  232. latestCategoryDebugLogLevel = new CategoryDebugLevel()
  233. {
  234. Category = category,
  235. LogLevel = debugLogLevel
  236. };
  237. // Cache this debug level for future queries.
  238. lock (categoryDebugLevelLock)
  239. {
  240. //Populate Level 4 cache
  241. categoryDebugLevel[category] = debugLogLevel;
  242. }
  243. }
  244. return debugLogLevel;
  245. }
  246. #endregion
  247. #region Methods: Add
  248. /// <summary>
  249. /// Adds a new object to the debug log entry.
  250. /// </summary>
  251. /// <param name="obj">The log object that are added.</param>
  252. [SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "obj")]
  253. public void Add(object obj)
  254. {
  255. if (disposed)
  256. return;
  257. if (logPersistentInfo)
  258. LogPersistentInfo();
  259. logger.LogDebug(obj.ToString());
  260. //Logger.AddEntry(new DebugLogEntry(entity, obj));
  261. }
  262. /// <summary>
  263. /// Adds the object if debuglogger is active with the default category and detaillevel.
  264. /// </summary>
  265. /// <param name="args"></param>
  266. /// <param name="formatString"></param>
  267. public void AddIfActive(string formatString, params object[] args)
  268. {
  269. if (IsActive())
  270. {
  271. try
  272. {
  273. Add(string.Format(formatString, args));
  274. }
  275. catch (FormatException fe)
  276. {
  277. Add(fe);
  278. Add(formatString);
  279. }
  280. }
  281. }
  282. /// <summary>
  283. /// Adds the object if debuglogger is active with the default category and detaillevel Detailed .
  284. /// </summary>
  285. /// <param name="formatString"></param>
  286. /// <param name="args"></param>
  287. public void AddIfActiveDetailed(string formatString, params object[] args)
  288. {
  289. if (IsActive(DebugLogLevel.Detailed))
  290. {
  291. try
  292. {
  293. Add(string.Format(formatString, args), DebugLogLevel.Detailed);
  294. }
  295. catch (FormatException fe)
  296. {
  297. Add(fe);
  298. Add(formatString);
  299. }
  300. }
  301. }
  302. /// <summary>
  303. /// Adds a new object to the debug log entry.
  304. /// </summary>
  305. /// <param name="obj">The log object that are added.</param>
  306. /// <param name="level">TDB</param>
  307. public void Add(object obj, DebugLogLevel level)
  308. {
  309. if (disposed)
  310. return;
  311. if (logPersistentInfo)
  312. LogPersistentInfo();
  313. //Logger.AddEntry(new DebugLogEntry(entity, obj, level));
  314. }
  315. /// <summary>
  316. /// Adds a new object to the debug log entry.
  317. /// </summary>
  318. /// <param name="obj">The log object that are added.</param>
  319. /// <param name="category">A specific category that this log is about.</param>
  320. [SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "obj")]
  321. public void Add(object obj, object category)
  322. {
  323. if (disposed)
  324. return;
  325. if (logPersistentInfo)
  326. LogPersistentInfo();
  327. //Logger.AddEntry(new DebugLogEntry(entity, obj, category));
  328. }
  329. /// <summary>
  330. /// Adds a new object to the debug log entry.
  331. /// </summary>
  332. /// <param name="obj">The log object that are added.</param>
  333. /// <param name="category">A specific category that this log is about.</param>
  334. /// <param name="level">TDB</param>
  335. [SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "obj")]
  336. public void Add(object obj, object category, DebugLogLevel level)
  337. {
  338. if (disposed)
  339. return;
  340. if (logPersistentInfo)
  341. LogPersistentInfo();
  342. //Logger.AddEntry(new DebugLogEntry(entity, obj, category, level));
  343. }
  344. /// <summary>
  345. ///
  346. /// </summary>
  347. private void LogPersistentInfo()
  348. {
  349. //Logger.AddEntry(new DebugLogEntry(entity, "Hooked persistent DebugLogger to logfile: " + IdentifiableEntity.ToString(entity, true), string.Empty, DebugLogLevel.Normal));
  350. logger.LogDebug("Hooked persistent DebugLogger to logfile: " + IdentifiableEntity.ToString(entity, true));
  351. logPersistentInfo = false;
  352. }
  353. #endregion
  354. #region Internal Methods
  355. /// <summary>
  356. /// Invalidates the internal flags for configuration reading.
  357. /// </summary>
  358. internal void Invalidate()
  359. {
  360. lock (categoryDebugLevelLock)
  361. {
  362. categoryDebugLevel.Clear();
  363. }
  364. }
  365. /// <summary>
  366. /// Static method to get the highest of two DebugLogLevel's.
  367. /// </summary>
  368. /// <param name="level1"></param>
  369. /// <param name="level2"></param>
  370. /// <returns></returns>
  371. public static DebugLogLevel MaxDebugLogLevel(DebugLogLevel level1, DebugLogLevel level2)
  372. {
  373. if (level1 > level2)
  374. return level1;
  375. else
  376. return level2;
  377. }
  378. #endregion
  379. #region IIdentifiableEntity Members
  380. /// <summary>
  381. /// The Id of the Entity.
  382. /// </summary>
  383. public int Id
  384. {
  385. get { return entity.Id; }
  386. }
  387. /// <summary>
  388. /// The EntityType of the Entity.
  389. /// </summary>
  390. public string EntityType
  391. {
  392. get { return entity.EntityType; }
  393. }
  394. /// <summary>
  395. /// This is used by the logger and should never be set by implementing classes
  396. /// </summary>
  397. public string FullEntityName { get; set; }
  398. /// <summary>
  399. /// The EntitySubType of the Entity.
  400. /// </summary>
  401. public string EntitySubType
  402. {
  403. get { return entity.EntitySubType; }
  404. }
  405. /// <summary>
  406. /// The ParentEntity of the Entity.
  407. /// </summary>
  408. public IIdentifiableEntity ParentEntity
  409. {
  410. get { return entity.ParentEntity; }
  411. }
  412. #endregion
  413. }
  414. }