using System; using System.Collections.Generic; using System.Threading; namespace Wayne.Lib.Log { internal class LoggerThread : IDisposable { #region Fields private readonly Thread thread; private readonly AutoResetEvent waitEvent; private readonly Queue entryQueue = new Queue(); private readonly object entryQueueLock = new object(); private readonly object logWritingLock = new object(); private bool threadShouldExit; // Flag that is used to indicate to the thread that it should exit gracefully. private bool disposed; private readonly LogConfig logConfig; #endregion #region Construction & Finalizer /// /// Initializes a new instance of the Logger thread and starts it. /// /// /// public LoggerThread(LogType logType, LogConfig logConfig) { this.logConfig = logConfig; //Create the wait event waitEvent = new AutoResetEvent(false); //Create the thread. thread = new Thread(Execute) { Priority = ThreadPriority.Lowest }; switch (logType) { case LogType.Debug: thread.Name = "Debug Logger thread"; break; case LogType.Event: thread.Name = "Event Logger thread"; break; } thread.IsBackground = true; thread.Start(); } /// /// Finalizer /// ~LoggerThread() { Dispose(false); } #endregion #region IDisposable Members /// /// Disposes the resources owned by the logger thread. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Internal dispose method. /// /// private void Dispose(bool disposing) { if (!disposed) { disposed = true; if (disposing) { threadShouldExit = true; waitEvent.Set(); waitEvent.Close(); thread.Join(); } } } #endregion #region The thread method /// /// The main function for the logger thread. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")] private void Execute() { while (!threadShouldExit) { try { // Wait for an event. waitEvent.WaitOne(); if (!disposed) { // Clear the entry queue. bool queueEmpty; do { // Pick the next logEntry in the queue safely in a locked way. LogEntry logEntry = null; lock (entryQueueLock) { if (entryQueue.Count > 0) logEntry = entryQueue.Dequeue(); queueEmpty = (entryQueue.Count == 0); } // Was there a log entry for me? if (!disposed && (logEntry != null)) PerformLog(logEntry); } while (!queueEmpty); } } catch (Exception exception) { Logger.FireOnThreadException(new LogException(LogExceptionType.GeneralThreadException, "An Exception is caught in the Logger.Execute() method. See inner exception!", exception)); } #if WindowsCE catch { Logger.FireOnThreadException(new LogException(LogExceptionType.GeneralThreadException, "An unknown exception is caught in the Logger.Execute() method.")); } #endif } } /// /// Performs the actual logging. /// /// private void PerformLog(LogEntry logEntry) { lock (logWritingLock) { LogWriter[] logWriters; DebugMarkerLogEntry debugMarkerLogEntry = logEntry as DebugMarkerLogEntry; if (debugMarkerLogEntry != null) { // Get all active LogWriters. logWriters = logConfig.GetLogWriters(); } else { // Get a list of the LogWriters that wants to log this entry. logWriters = logConfig.GetLogWriters(logEntry.EntityCategory); } // Call them in turn and write the log entry. foreach (LogWriter writer in logWriters) writer.Write(logEntry); } } #endregion #region Public Methods /// /// Adds an entry that should be handled by the logger thread. /// /// public void AddEntry(LogEntry logEntry) { if (disposed) return; if (Logger.Synchronized) { PerformLog(logEntry); } else { lock (entryQueueLock) { entryQueue.Enqueue(logEntry); } waitEvent.Set(); } } #endregion } }