Logger.cs 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics.CodeAnalysis;
  4. using System.IO;
  5. using System.Net;
  6. using System.Net.Sockets;
  7. using System.Text;
  8. using Wayne.Lib.IO;
  9. namespace Wayne.Lib.Log
  10. {
  11. /// <summary>
  12. /// Logger is a static class used to create log objects.
  13. /// </summary>
  14. public static class Logger
  15. {
  16. #region Fields
  17. /// <summary>
  18. /// Default date and time format to use in log files.
  19. /// </summary>
  20. public const string DefaultDateTimeFormat = "HH':'mm':'ss'.'fff'[#]'";
  21. private const int DefaultDebugMarkerBroadcastPort = 11001;
  22. private static readonly List<DebugLogger> OutstandingLogObjects = new List<DebugLogger>();
  23. private static readonly object OutstandingLogObjectsLock = new object(); // Locking object to safely access the OutstandingLogObjects.
  24. private static readonly Dictionary<string, IEventSubscriber> EventSubscriberDict = new Dictionary<string, IEventSubscriber>();
  25. private static readonly object EventSubscriberDictSyncObj = new object();
  26. private static readonly Dictionary<string, ExternalLogWriter> ExternalLogWriterNameDict = new Dictionary<string, ExternalLogWriter>();
  27. private static readonly Dictionary<ExternalLogWriterWrapper, ExternalLogWriter> ExternalLogWriterWrapperDict = new Dictionary<ExternalLogWriterWrapper, ExternalLogWriter>();
  28. private static readonly object ExternalLogWriterDictSyncObj = new object();
  29. private static readonly byte[] DebugMarkerBroadcastReceiverBuffer = new byte[100];
  30. private static readonly List<string> DebugConfigFileNameList = new List<string>();
  31. private static readonly List<string> EventConfigFileNameList = new List<string>();
  32. private static readonly List<string[]> DebugConfigFilesList = new List<string[]>();
  33. private static readonly List<string[]> EventConfigFilesList = new List<string[]>();
  34. private static readonly List<LogConfigBuilder> DebugConfigBuilderList = new List<LogConfigBuilder>();
  35. private static readonly List<LogConfigBuilder> EventConfigBuilderList = new List<LogConfigBuilder>();
  36. private static readonly List<LogConfigOutput> LeftoverLinesLogConfigOutputs = new List<LogConfigOutput>();
  37. private static readonly List<LogConfigOutput> LeftoverEntitiesLogConfigOutputs = new List<LogConfigOutput>();
  38. private static bool active;
  39. private static LoggerThread loggerThread;
  40. private static LoggerThread eventLoggerThread;
  41. private static LogConfig debugConfig;
  42. private static LogConfig eventConfig;
  43. private static DotNetLog dotNetLog;
  44. private static GlobalLogEntryCounter globalLogEntryCounter;
  45. private static IPEndPoint debugMarkerBroadcastAddress;
  46. private static System.Net.Sockets.Socket debugMarkerBroadcastReceiverSocket;
  47. #endregion
  48. #region Construction
  49. static Logger()
  50. {
  51. Reset();
  52. }
  53. #endregion
  54. #region Internal Properties
  55. /// <summary>
  56. /// Tells whether someone has called the Close() method.
  57. /// </summary>
  58. public static bool IsClosed
  59. {
  60. get { return !active; }
  61. }
  62. /// <summary>
  63. /// Internal access to the DebugConfig.
  64. /// </summary>
  65. internal static LogConfig DebugConfig
  66. {
  67. get { return debugConfig; }
  68. }
  69. /// <summary>
  70. /// Internal access to the GlobalLogEntryCounter.
  71. /// </summary>
  72. internal static GlobalLogEntryCounter GlobalLogEntryCounter
  73. {
  74. get { return globalLogEntryCounter; }
  75. }
  76. #endregion
  77. #region Internal Methods
  78. /// <summary>
  79. /// This method is used to fire an event instead of throwing an exception if something
  80. /// "crashes" in a thread (in order to keep the thread alive but still "report" exceptions).
  81. /// </summary>
  82. /// <param name="exception"></param>
  83. [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
  84. [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")]
  85. internal static void FireOnThreadException(LogException exception)
  86. {
  87. try
  88. {
  89. if (OnThreadException != null)
  90. OnThreadException(null, new EventArgs<LogException>(exception));
  91. }
  92. catch (Exception) { }
  93. }
  94. /// <summary>
  95. /// Internal method that is used by the EventLogSubscriptionLogWriter to publish an
  96. /// event entry to a specific subscriber.
  97. /// </summary>
  98. /// <param name="eventSubscriberId"></param>
  99. /// <param name="eventLogEntry"></param>
  100. /// <param name="storageType"></param>
  101. internal static void PublishEventLog(string eventSubscriberId, EventLogEntry eventLogEntry,
  102. LogConfigEventLogStorageType storageType)
  103. {
  104. //eventLogStorage.Add(eventSubscriberId, eventLogEntry, storageType);
  105. IEventSubscriber eventSubscriber;
  106. lock (EventSubscriberDictSyncObj)
  107. {
  108. if (!EventSubscriberDict.TryGetValue(eventSubscriberId, out eventSubscriber))
  109. eventSubscriber = null;
  110. }
  111. if (eventSubscriber != null)
  112. eventSubscriber.HandleEvent(eventLogEntry);
  113. }
  114. #endregion
  115. #region Internal Methods: Persistent log objects
  116. internal static void RegisterPersistentLogObject(DebugLogger debugLogger)
  117. {
  118. lock (OutstandingLogObjectsLock)
  119. {
  120. if (!OutstandingLogObjects.Contains(debugLogger))
  121. OutstandingLogObjects.Add(debugLogger);
  122. }
  123. }
  124. internal static void UnregisterPersistentLogObject(DebugLogger debugLogger)
  125. {
  126. lock (OutstandingLogObjectsLock)
  127. {
  128. if (OutstandingLogObjects.Contains(debugLogger))
  129. OutstandingLogObjects.Remove(debugLogger);
  130. }
  131. }
  132. #endregion
  133. #region Public Events
  134. /// <summary>
  135. /// An event that is fired when the logging thread is catching an exception.
  136. /// </summary>
  137. public static event EventHandler<Wayne.Lib.EventArgs<LogException>> OnThreadException;
  138. #endregion
  139. #region Public Properties
  140. /// <summary>
  141. /// The current debug log configuration file.
  142. /// </summary>
  143. [Obsolete("This property is no longer supported. Use the property DebugLoggingConfigured to check whether it's active or not.", true)]
  144. public static string DebugConfigFileName
  145. {
  146. get { return string.Empty; }
  147. }
  148. /// <summary>
  149. /// The current event log configuration file.
  150. /// </summary>
  151. [Obsolete("This property is no longer supported. Use the property EventLoggingConfigured to check whether it's active or not.", true)]
  152. public static string EventConfigFileName
  153. {
  154. get { return string.Empty; }
  155. }
  156. /// <summary>
  157. /// Is any debug logging configured?
  158. /// </summary>
  159. public static bool DebugLoggingConfigured
  160. {
  161. get { return debugConfig.IsConfigured; }
  162. }
  163. /// <summary>
  164. /// Is any event logging configured?
  165. /// </summary>
  166. public static bool EventLoggingConfigured
  167. {
  168. get { return eventConfig.IsConfigured; }
  169. }
  170. /// <summary>
  171. /// Should the logger be synchronized or not?
  172. /// Default is false (the log writing is performed in another thread).
  173. /// </summary>
  174. public static bool Synchronized { get; set; }
  175. #endregion
  176. #region Public Methods: ConfigFile handling
  177. /// <summary>
  178. /// Clear all loaded log configuration .
  179. /// </summary>
  180. public static void ClearConfigFiles()
  181. {
  182. if (!active)
  183. return;
  184. DebugConfigFileNameList.Clear();
  185. EventConfigFileNameList.Clear();
  186. DebugConfigFilesList.Clear();
  187. EventConfigFilesList.Clear();
  188. DebugConfigBuilderList.Clear();
  189. EventConfigBuilderList.Clear();
  190. LeftoverLinesLogConfigOutputs.Clear();
  191. LeftoverEntitiesLogConfigOutputs.Clear();
  192. RefreshConfig();
  193. }
  194. /// <summary>
  195. /// Reloads the configuration from the specified configuration file.
  196. /// </summary>
  197. /// <param name="debugConfigFileName">Log configuration for the debug logging.</param>
  198. /// <param name="eventConfigFileName">Log configuration for the event logging.</param>
  199. public static void SetConfigFile(string debugConfigFileName, string eventConfigFileName)
  200. {
  201. if (!active)
  202. return;
  203. ClearConfigFiles();
  204. AddDebugConfigFile(debugConfigFileName);
  205. AddEventConfigFile(eventConfigFileName);
  206. }
  207. /// <summary>
  208. /// Reloads the configuration from the specified configuration file for the debug logging. To activate the event logging
  209. /// SetConfigFile(string,string) should be called.
  210. /// </summary>
  211. /// <param name="debugConfigFileName">Log configuration for the debug logging.</param>
  212. public static void SetConfigFile(string debugConfigFileName)
  213. {
  214. if (!active)
  215. return;
  216. ClearConfigFiles();
  217. AddDebugConfigFile(debugConfigFileName);
  218. }
  219. /// <summary>
  220. /// Reloads the configuration, adding the given debug log config file.
  221. /// </summary>
  222. /// <param name="debugConfigFileName">Log configuration for the debug logging.</param>
  223. public static void AddDebugConfigFile(string debugConfigFileName)
  224. {
  225. if (!active)
  226. return;
  227. DebugConfigFileNameList.Add(debugConfigFileName);
  228. RefreshConfig();
  229. }
  230. /// <summary>
  231. /// Reloads the configuration, adding the given debug log config file.
  232. /// </summary>
  233. /// <param name="logConfigBuilders">Log configurations for the debug logging.</param>
  234. public static void AddDebugConfigFile(params LogConfigBuilder[] logConfigBuilders)
  235. {
  236. if (!active)
  237. return;
  238. DebugConfigBuilderList.AddRange(logConfigBuilders);
  239. RefreshConfig();
  240. }
  241. /// <summary>
  242. /// Reloads the configuration, adding the given debug log config file.
  243. /// </summary>
  244. /// <param name="debugConfigFileStream">Log configuration for the debug logging.</param>
  245. public static void AddDebugConfigFile(Stream debugConfigFileStream)
  246. {
  247. AddDebugConfigFile(debugConfigFileStream, Encoding.UTF8);
  248. }
  249. /// <summary>
  250. /// Reloads the configuration, adding the given debug log config file.
  251. /// </summary>
  252. /// <param name="debugConfigFileStream">Log configuration for the debug logging.</param>
  253. /// <param name="encoding">The encoding of the stream.</param>
  254. public static void AddDebugConfigFile(Stream debugConfigFileStream, Encoding encoding)
  255. {
  256. if (!active)
  257. return;
  258. DebugConfigFilesList.Add(GetFileLines(debugConfigFileStream, encoding));
  259. RefreshConfig();
  260. }
  261. /// <summary>
  262. /// Reloads the configuration, adding the given event log config file.
  263. /// </summary>
  264. /// <param name="eventConfigFileName">Log configuration for the event logging.</param>
  265. public static void AddEventConfigFile(string eventConfigFileName)
  266. {
  267. if (!active)
  268. return;
  269. EventConfigFileNameList.Add(eventConfigFileName);
  270. RefreshConfig();
  271. }
  272. /// <summary>
  273. /// Reloads the configuration, adding the given event log config file.
  274. /// </summary>
  275. /// <param name="logConfigBuilders">Log configurations for the event logging.</param>
  276. public static void AddEventConfigFile(params LogConfigBuilder[] logConfigBuilders)
  277. {
  278. if (!active)
  279. return;
  280. EventConfigBuilderList.AddRange(logConfigBuilders);
  281. RefreshConfig();
  282. }
  283. /// <summary>
  284. /// Reloads the configuration, adding the given event log config file.
  285. /// </summary>
  286. /// <param name="eventConfigFileStream">Log configuration for the event logging.</param>
  287. public static void AddEventConfigFile(Stream eventConfigFileStream)
  288. {
  289. AddDebugConfigFile(eventConfigFileStream, Encoding.UTF8);
  290. }
  291. /// <summary>
  292. /// Reloads the configuration, adding the given event log config file.
  293. /// </summary>
  294. /// <param name="eventConfigFileStream">Log configuration for the event logging.</param>
  295. /// <param name="encoding">The encoding of the stream.</param>
  296. public static void AddEventConfigFile(Stream eventConfigFileStream, Encoding encoding)
  297. {
  298. if (!active)
  299. return;
  300. EventConfigFilesList.Add(GetFileLines(eventConfigFileStream, encoding));
  301. RefreshConfig();
  302. }
  303. /// <summary>
  304. /// Reloads the configuration, adding the given leftover log line output.
  305. /// </summary>
  306. /// <param name="output">An output to receive leftover log lines.</param>
  307. public static void AddLeftoverLinesOutput(LogConfigOutput output)
  308. {
  309. if (!active)
  310. return;
  311. LeftoverLinesLogConfigOutputs.Add((LogConfigOutput)output.Clone());
  312. RefreshConfig();
  313. }
  314. /// <summary>
  315. /// Reloads the configuration, adding the given leftover log entity output.
  316. /// </summary>
  317. /// <param name="output">An output to receive leftover log entities.</param>
  318. public static void AddLeftoverEntitiesOutput(LogConfigOutput output)
  319. {
  320. if (!active)
  321. return;
  322. LeftoverEntitiesLogConfigOutputs.Add((LogConfigOutput)output.Clone());
  323. RefreshConfig();
  324. }
  325. private static string[] GetFileLines(Stream fileStream, Encoding encoding)
  326. {
  327. List<string> lines = new List<string>();
  328. using (StreamReader reader = new StreamReader(fileStream, encoding))
  329. {
  330. while (!reader.EndOfStream)
  331. lines.Add(reader.ReadLine());
  332. }
  333. return lines.ToArray();
  334. }
  335. #endregion
  336. #region Public Methods: Refresh
  337. /// <summary>
  338. /// Re-loads the configuration for the logging.
  339. /// </summary>
  340. [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
  341. [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")]
  342. public static void RefreshConfig()
  343. {
  344. if (!active)
  345. return;
  346. InitDebugMarkerBroadcastListener();
  347. // Refresh the DebugConfig.
  348. List<string[]> debugConfigFiles = new List<string[]>(DebugConfigFilesList);
  349. foreach (string fileName in DebugConfigFileNameList)
  350. debugConfigFiles.Add(FileSupport.LoadToStringArray(fileName));
  351. if (DebugConfigBuilderList.Count > 0)
  352. debugConfigFiles.Add(LogConfigBuilder.GetLogConfigFileLines(DebugConfigBuilderList.ToArray()));
  353. DebugConfig.Refresh(debugConfigFiles, LeftoverLinesLogConfigOutputs.ToArray(), LeftoverEntitiesLogConfigOutputs.ToArray());
  354. // Refresh the EventConfig.
  355. List<string[]> eventConfigFiles = new List<string[]>(EventConfigFilesList);
  356. foreach (string fileName in EventConfigFileNameList)
  357. eventConfigFiles.Add(FileSupport.LoadToStringArray(fileName));
  358. if (EventConfigBuilderList.Count > 0)
  359. eventConfigFiles.Add(LogConfigBuilder.GetLogConfigFileLines(EventConfigBuilderList.ToArray()));
  360. eventConfig.Refresh(eventConfigFiles, new LogConfigOutput[0], new LogConfigOutput[0]);
  361. // Start the thread if it's not yet started (first time it's null).
  362. if ((loggerThread == null) && (debugConfigFiles.Count > 0))
  363. loggerThread = new LoggerThread(LogType.Debug, debugConfig);
  364. if ((eventLoggerThread == null) && (eventConfigFiles.Count > 0))
  365. eventLoggerThread = new LoggerThread(LogType.Event, eventConfig);
  366. // To prevent locking the list while iterating, take a copy and iterate through the copy.
  367. // The danger of doing this is that an object could be removed after the copy and before its
  368. // Invalidate-method is called. But nothing really bad can happen due to this...
  369. List<DebugLogger> outstandingLogObjectsCopy;
  370. lock (OutstandingLogObjectsLock)
  371. {
  372. outstandingLogObjectsCopy = new List<DebugLogger>(OutstandingLogObjects);
  373. }
  374. foreach (DebugLogger debugLogger in outstandingLogObjectsCopy)
  375. {
  376. try
  377. {
  378. debugLogger.Invalidate();
  379. }
  380. catch (Exception) { }
  381. }
  382. // Check if the dotNetLog should be active or not.
  383. dotNetLog.CheckActive();
  384. }
  385. #endregion
  386. #region Public Methods: Reset
  387. /// <summary>
  388. /// Resets the logger completely.
  389. /// </summary>
  390. public static void Reset()
  391. {
  392. Close();
  393. active = true;
  394. debugConfig = new LogConfig();
  395. eventConfig = new LogConfig();
  396. dotNetLog = new DotNetLog();
  397. globalLogEntryCounter = new GlobalLogEntryCounter();
  398. }
  399. #endregion
  400. #region Public Methods: Close
  401. /// <summary>
  402. /// Closes the logger. This should be done as the last things before the application terminates.
  403. /// </summary>
  404. public static void Close()
  405. {
  406. if (active)
  407. {
  408. // Shut down the config.
  409. if (debugConfig != null)
  410. {
  411. debugConfig.Dispose();
  412. debugConfig = null;
  413. }
  414. if (eventConfig != null)
  415. {
  416. eventConfig.Dispose();
  417. eventConfig = null;
  418. }
  419. if (dotNetLog != null)
  420. {
  421. // Shut down the dotNetLog.
  422. dotNetLog.Dispose();
  423. dotNetLog = null;
  424. }
  425. ExternalLogWriterNameDict.Clear();
  426. ExternalLogWriterWrapperDict.Clear();
  427. // Now we're closed.
  428. active = false;
  429. // Shut down the loggerThread.
  430. if (loggerThread != null)
  431. {
  432. loggerThread.Dispose();
  433. loggerThread = null;
  434. }
  435. if (eventLoggerThread != null)
  436. {
  437. eventLoggerThread.Dispose();
  438. eventLoggerThread = null;
  439. }
  440. if (debugMarkerBroadcastReceiverSocket != null)
  441. {
  442. try
  443. {
  444. if (debugMarkerBroadcastReceiverSocket.Connected)
  445. {
  446. debugMarkerBroadcastReceiverSocket.Shutdown(SocketShutdown.Both);
  447. }
  448. }
  449. catch { }
  450. try
  451. {
  452. debugMarkerBroadcastReceiverSocket.Close();
  453. }
  454. catch { }
  455. debugMarkerBroadcastReceiverSocket = null;
  456. }
  457. if (globalLogEntryCounter != null)
  458. {
  459. globalLogEntryCounter.Dispose();
  460. globalLogEntryCounter = null;
  461. }
  462. debugMarkerBroadcastAddress = null;
  463. DebugConfigFileNameList.Clear();
  464. DebugConfigFilesList.Clear();
  465. EventConfigFileNameList.Clear();
  466. EventConfigFilesList.Clear();
  467. DebugConfigBuilderList.Clear();
  468. EventConfigBuilderList.Clear();
  469. OutstandingLogObjects.Clear();
  470. EventSubscriberDict.Clear();
  471. LeftoverLinesLogConfigOutputs.Clear();
  472. LeftoverEntitiesLogConfigOutputs.Clear();
  473. Synchronized = false;
  474. }
  475. }
  476. #endregion
  477. #region Public Methods: LogEntry
  478. /// <summary>
  479. /// Logs the given LogEntry.
  480. /// </summary>
  481. /// <param name="logEntry">The LogEntry to log.</param>
  482. public static void AddEntry(LogEntry logEntry)
  483. {
  484. if (!active)
  485. return;
  486. if (loggerThread != null)
  487. loggerThread.AddEntry(logEntry);
  488. if ((eventLoggerThread != null) && (logEntry is EventLogEntry))
  489. eventLoggerThread.AddEntry(logEntry); // eventThread has higher priority?
  490. }
  491. /// <summary>
  492. /// Logs the given ExceptionLogEntry.
  493. /// </summary>
  494. /// <param name="logEntry">The LogEntry to log.</param>
  495. public static void AddExceptionLogEntry(ExceptionLogEntry logEntry)
  496. {
  497. AddEntry(logEntry);
  498. }
  499. /// <summary>
  500. /// Logs the given ErrorLogEntry.
  501. /// </summary>
  502. /// <param name="logEntry">The LogEntry to log.</param>
  503. public static void AddErrorLogEntry(ErrorLogEntry logEntry)
  504. {
  505. AddEntry(logEntry);
  506. }
  507. /// <summary>
  508. /// Logs the given EventLogEntry.
  509. /// </summary>
  510. /// <param name="logEntry">The LogEntry to log.</param>
  511. public static void AddEventLogEntry(EventLogEntry logEntry)
  512. {
  513. AddEntry(logEntry);
  514. }
  515. #endregion
  516. #region Public Methods: Unregister entity
  517. /// <summary>
  518. /// Removes the specified entity from the internal filter buffers.
  519. /// </summary>
  520. /// <param name="entity"></param>
  521. public static void UnregisterEntity(IIdentifiableEntity entity)
  522. {
  523. if (debugConfig != null)
  524. debugConfig.UnregisterEntity(entity);
  525. if (eventConfig != null)
  526. eventConfig.UnregisterEntity(entity);
  527. }
  528. #endregion
  529. #region Public Methods for Event Subscribers
  530. /// <summary>
  531. /// Remove an event log from the storage. This method should be called from a registered IEventSubscriber when
  532. /// it has handled an event.
  533. /// </summary>
  534. /// <param name="eventSubscriber"></param>
  535. /// <param name="eventLogEntry"></param>
  536. public static void EventLogHandled(IEventSubscriber eventSubscriber, EventLogEntry eventLogEntry)
  537. {
  538. //eventLogStorage.Remove(eventSubscriber, eventLogEntry);
  539. }
  540. /// <summary>
  541. /// Register an IEventSubscriber, so it can start receiving events. The event subscriber will be sent the pending events that
  542. /// has been stored since the subscriber was registered the last time.
  543. /// </summary>
  544. /// <param name="eventSubscriber"></param>
  545. public static void RegisterEventSubscriber(IEventSubscriber eventSubscriber)
  546. {
  547. if (eventSubscriber == null)
  548. throw new ArgumentNullException("eventSubscriber");
  549. lock (EventSubscriberDictSyncObj)
  550. {
  551. EventSubscriberDict[eventSubscriber.SubscriberId] = eventSubscriber;
  552. }
  553. //EventLogEntry[] entries = eventLogStorage.GetPendingEvents(eventSubscriber.SubscriberId);
  554. //foreach (EventLogEntry entry in entries)
  555. // eventSubscriber.HandleEvent(entry);
  556. }
  557. /// <summary>
  558. /// Unregister a registered IEventSubscriber.
  559. /// </summary>
  560. /// <param name="eventSubscriber"></param>
  561. public static void UnregisterEventSubscriber(IEventSubscriber eventSubscriber)
  562. {
  563. if (eventSubscriber == null)
  564. throw new ArgumentNullException("eventSubscriber");
  565. lock (EventSubscriberDictSyncObj)
  566. {
  567. EventSubscriberDict.Remove(eventSubscriber.SubscriberId);
  568. }
  569. }
  570. #endregion
  571. #region ExternalLogging
  572. private static string GetExternalLogWriterKey(ExternalLogWriter externalLogWriter)
  573. {
  574. return string.Format("{0}#{1}", externalLogWriter.ExternalLogType, externalLogWriter.ExternalLogName);
  575. }
  576. private static string GetExternalLogWriterKey(ExternalLogWriterWrapper externalLogWriterWrapper)
  577. {
  578. return string.Format("{0}#{1}", externalLogWriterWrapper.ExternalLogType, externalLogWriterWrapper.ExternalLogName);
  579. }
  580. /// <summary>
  581. /// Register an ExternalLogWriter.
  582. /// </summary>
  583. /// <param name="externalLogWriter">The external log writer to register.</param>
  584. public static void RegisterExternalLogger(ExternalLogWriter externalLogWriter)
  585. {
  586. if (ExternalLogWriterDictSyncObj != null)
  587. lock (ExternalLogWriterDictSyncObj)
  588. ExternalLogWriterNameDict[GetExternalLogWriterKey(externalLogWriter)] = externalLogWriter;
  589. }
  590. /// <summary>
  591. /// Unregister an ExternalLogWriter.
  592. /// </summary>
  593. /// <param name="externalLogWriter">The external log writer to unregister.</param>
  594. public static void UnregisterExternalLogger(ExternalLogWriter externalLogWriter)
  595. {
  596. if (ExternalLogWriterDictSyncObj != null)
  597. lock (ExternalLogWriterDictSyncObj)
  598. {
  599. ExternalLogWriterNameDict.Remove(GetExternalLogWriterKey(externalLogWriter));
  600. bool found;
  601. do
  602. {
  603. found = false;
  604. foreach (KeyValuePair<ExternalLogWriterWrapper, ExternalLogWriter> pair in ExternalLogWriterWrapperDict)
  605. {
  606. if (pair.Value == externalLogWriter)
  607. {
  608. ExternalLogWriterWrapperDict.Remove(pair.Key);
  609. found = true;
  610. break;
  611. }
  612. }
  613. }
  614. while (found);
  615. }
  616. }
  617. [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
  618. internal static void PerformExternalLogEntry(ExternalLogWriterWrapper externalLogWriterWrapper, LogEntry logEntry)
  619. {
  620. if (ExternalLogWriterDictSyncObj != null)
  621. {
  622. lock (ExternalLogWriterDictSyncObj)
  623. {
  624. ExternalLogWriter externalLogWriter;
  625. if (!ExternalLogWriterWrapperDict.TryGetValue(externalLogWriterWrapper, out externalLogWriter))
  626. {
  627. if (ExternalLogWriterNameDict.TryGetValue(GetExternalLogWriterKey(externalLogWriterWrapper), out externalLogWriter))
  628. {
  629. externalLogWriter.InitParametersInternal(externalLogWriterWrapper.Parameters);
  630. ExternalLogWriterWrapperDict[externalLogWriterWrapper] = externalLogWriter;
  631. }
  632. }
  633. if (externalLogWriter != null)
  634. {
  635. try
  636. {
  637. if (externalLogWriter.Active)
  638. {
  639. string text = LogTextWriting.GetLogEntryText(externalLogWriterWrapper, logEntry,
  640. externalLogWriterWrapper.WritingParameters);
  641. externalLogWriter.LogInternal(logEntry, text);
  642. }
  643. }
  644. catch { }
  645. }
  646. }
  647. }
  648. }
  649. /// <summary>
  650. /// Get the LogTextWritingParameters for the requested ExternalLogWriter.
  651. /// </summary>
  652. /// <param name="externalLogWriter">The ExternalLogWriter to get the parameters of.</param>
  653. [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#")]
  654. internal static LogTextWritingParameters GetExternalLoggerWritingParameters(ExternalLogWriter externalLogWriter)
  655. {
  656. if (ExternalLogWriterDictSyncObj != null)
  657. {
  658. lock (ExternalLogWriterDictSyncObj)
  659. {
  660. foreach (KeyValuePair<ExternalLogWriterWrapper, ExternalLogWriter> pair in ExternalLogWriterWrapperDict)
  661. {
  662. if (pair.Value == externalLogWriter)
  663. {
  664. return pair.Key.WritingParameters;
  665. }
  666. }
  667. }
  668. }
  669. return null;
  670. }
  671. #endregion
  672. #region Debug Marker Broadcasting
  673. /// <summary>
  674. /// Broadcasts a debug marker to be inserted into all active logfiles.
  675. /// </summary>
  676. /// <param name="debugMarker">The text to insert into all the log files.</param>
  677. // public static void InjectDebugMarker(string debugMarker)
  678. // {
  679. // try
  680. // {
  681. // System.Net.Sockets.Socket transmitterSocket = null;
  682. // try
  683. // {
  684. // try
  685. // {
  686. //transmitterSocket = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.IP);
  687. // transmitterSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
  688. // var lingerOption = new LingerOption(true, 0);
  689. // transmitterSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, lingerOption);
  690. // transmitterSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, 1);
  691. // transmitterSocket.Bind(new IPEndPoint(IPAddress.Any, 0)); // Bind the socket to the "any" end point.
  692. // byte[] data = Encoding.UTF8.GetBytes(debugMarker);
  693. // int offset = 0;
  694. // int size = data.Length;
  695. // if (size > DebugMarkerBroadcastReceiverBuffer.Length)
  696. // size = DebugMarkerBroadcastReceiverBuffer.Length;
  697. // int currentSentSize;
  698. // do
  699. // {
  700. // currentSentSize = transmitterSocket.SendTo(data, offset, size, SocketFlags.None, GetDebugMarkerBroadcastIPEndPoint());
  701. // offset += currentSentSize;
  702. // size -= currentSentSize;
  703. // }
  704. // while ((size > 0) && (currentSentSize > 0));
  705. // }
  706. // catch (Exception) { }
  707. // }
  708. // finally
  709. // {
  710. // if (transmitterSocket != null)
  711. // {
  712. // try
  713. // {
  714. // transmitterSocket.Shutdown(SocketShutdown.Both);
  715. // }
  716. // finally
  717. // {
  718. // transmitterSocket.Close();
  719. // }
  720. // }
  721. // }
  722. // }
  723. // catch (Exception) { }
  724. // }
  725. private static void InitDebugMarkerBroadcastListener()
  726. {
  727. if (debugMarkerBroadcastReceiverSocket == null)
  728. {
  729. try
  730. {
  731. debugMarkerBroadcastReceiverSocket = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.IP);
  732. debugMarkerBroadcastReceiverSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1);
  733. //debugMarkerBroadcastReceiverSocket.Bind(new IPEndPoint(IPAddress.Any, GetDebugMarkerBroadcastIPEndPoint().Port)); // Bind the socket to the "any" end point.
  734. debugMarkerBroadcastReceiverSocket.BeginReceive(DebugMarkerBroadcastReceiverBuffer, 0, DebugMarkerBroadcastReceiverBuffer.Length, 0, ReceiveCallback, debugMarkerBroadcastReceiverSocket); // Start to listen.
  735. }
  736. catch
  737. {
  738. try
  739. {
  740. debugMarkerBroadcastReceiverSocket.Close();
  741. }
  742. catch (Exception)
  743. {
  744. }
  745. debugMarkerBroadcastReceiverSocket = null;
  746. }
  747. }
  748. }
  749. private static void ReceiveCallback(IAsyncResult ar)
  750. {
  751. if (debugMarkerBroadcastReceiverSocket != null)
  752. {
  753. try
  754. {
  755. try
  756. {
  757. System.Net.Sockets.Socket socket = (System.Net.Sockets.Socket)ar.AsyncState;
  758. int bytesReceived = 0;
  759. try
  760. {
  761. try
  762. {
  763. bytesReceived = socket.EndReceive(ar);
  764. }
  765. catch { }
  766. }
  767. finally
  768. {
  769. if (bytesReceived > 0)
  770. {
  771. string debugMarker = Encoding.UTF8.GetString(DebugMarkerBroadcastReceiverBuffer, 0, bytesReceived);
  772. AddEntry(new DebugMarkerLogEntry(debugMarker));
  773. }
  774. }
  775. }
  776. finally
  777. {
  778. if (active)
  779. debugMarkerBroadcastReceiverSocket.BeginReceive(DebugMarkerBroadcastReceiverBuffer, 0, DebugMarkerBroadcastReceiverBuffer.Length, 0, ReceiveCallback, debugMarkerBroadcastReceiverSocket);
  780. }
  781. }
  782. catch { }
  783. }
  784. }
  785. //[System.Diagnostics.DebuggerStepThrough()]
  786. //private static IPEndPoint GetDebugMarkerBroadcastIPEndPoint()
  787. //{
  788. // try
  789. // {
  790. // if (debugMarkerBroadcastAddress == null)
  791. // {
  792. // using (Microsoft.Win32.RegistryKey key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"Software\Dresser Wayne\ISM Nucleus\TCP IP\UDP Broadcast", false))
  793. // {
  794. // IPAddress broadcastAddress;
  795. // int broadcastPort;
  796. // if (key != null)
  797. // {
  798. // broadcastAddress = IPAddress.Parse((string)key.GetValue("BroadcastAddress", "255.255.255.255"));
  799. // broadcastPort = (int)key.GetValue("LogMarkerInjectorPort", DefaultDebugMarkerBroadcastPort);
  800. // }
  801. // else
  802. // {
  803. // broadcastPort = DefaultDebugMarkerBroadcastPort;
  804. // broadcastAddress = IPAddress.Broadcast;
  805. // }
  806. // if (broadcastAddress != null)
  807. // debugMarkerBroadcastAddress = new IPEndPoint(broadcastAddress, broadcastPort);
  808. // }
  809. // }
  810. // }
  811. // catch
  812. // {
  813. // if (debugMarkerBroadcastAddress == null)
  814. // debugMarkerBroadcastAddress = new IPEndPoint(IPAddress.Broadcast, DefaultDebugMarkerBroadcastPort);
  815. // }
  816. // return debugMarkerBroadcastAddress;
  817. //}
  818. #endregion
  819. #region DateTimeToString
  820. internal static string DateTimeToString(DateTime dateTime, string dateTimeFormat)
  821. {
  822. if (GlobalLogEntryCounter != null)
  823. return DateTimeToString(dateTime, dateTimeFormat, GlobalLogEntryCounter.GetNextValue());
  824. return DateTimeToString(dateTime, dateTimeFormat, 0);
  825. }
  826. internal static string DateTimeToString(DateTime dateTime, string dateTimeFormat, UInt64 logEntryIndex)
  827. {
  828. return FormatDateTime(dateTime) + "[" + logEntryIndex.ToString("00000") + "]";
  829. }
  830. private static string FormatDateTime(DateTime dt)
  831. {
  832. char[] chars = new char[12];
  833. Write2Chars(chars, 0, dt.Hour);
  834. chars[2] = ':';
  835. Write2Chars(chars, 3, dt.Minute);
  836. chars[5] = ':';
  837. Write2Chars(chars, 6, dt.Second);
  838. chars[8] = '.';
  839. Write2Chars(chars, 9, dt.Millisecond / 10);
  840. chars[11] = Digit(dt.Millisecond % 10);
  841. return new string(chars);
  842. }
  843. private static void Write2Chars(char[] chars, int offset, int value)
  844. {
  845. chars[offset] = Digit(value / 10);
  846. chars[offset + 1] = Digit(value % 10);
  847. }
  848. private static char Digit(int value)
  849. {
  850. return (char)(value + '0');
  851. }
  852. #endregion
  853. }
  854. }