Logger.cs 35 KB

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