Changeset 1777


Ignore:
Timestamp:
2/9/2010 7:23:22 AM (4 years ago)
Author:
lowjoel
Message:

Redesigned the Logger paradigm -- instead of a dealing with a logger object to do any form of logging, we have a static Logger class which code can log to, and define log targets to receive those logs. Also included is a LogSession? class which sets the log target for the current thread or for a few threads. Addresses #320: Logging improvements

Location:
branches/eraser6/CodeReview
Files:
10 edited
1 moved

Legend:

Unmodified
Added
Removed
  • branches/eraser6/CodeReview/Eraser.DefaultPlugins/FileSystems/Windows.cs

    r1745 r1777  
    155155 
    156156        public override void EraseClusterTips(VolumeInfo info, ErasureMethod method, 
    157             Logger log, ClusterTipsSearchProgress searchCallback, 
    158             ClusterTipsEraseProgress eraseCallback) 
     157            ClusterTipsSearchProgress searchCallback, ClusterTipsEraseProgress eraseCallback) 
    159158        { 
    160159            //List all the files which can be erased. 
     
    163162                throw new InvalidOperationException(S._("Could not erase cluster tips in {0} " + 
    164163                    "as the volume is not mounted.", info.VolumeId)); 
    165             ListFiles(new DirectoryInfo(info.MountPoints[0]), files, log, searchCallback); 
     164            ListFiles(new DirectoryInfo(info.MountPoints[0]), files, searchCallback); 
    166165 
    167166            //For every file, erase the cluster tips. 
     
    180179                catch (UnauthorizedAccessException) 
    181180                { 
    182                     log.LastSessionEntries.Add(new LogEntry(S._("{0} did not have its " + 
    183                         "cluster tips erased because you do not have the required permissions to " + 
    184                         "erase the file cluster tips.", files[i]), LogLevel.Information)); 
     181                    Logger.Log(S._("{0} did not have its cluster tips erased because you do not " + 
     182                        "have the required permissions to erase the file cluster tips.", files[i]), 
     183                        LogLevel.Information); 
    185184                } 
    186185                catch (IOException e) 
    187186                { 
    188                     log.LastSessionEntries.Add(new LogEntry(S._("{0} did not have its " + 
    189                         "cluster tips erased. The error returned was: {1}", files[i], 
    190                         e.Message), LogLevel.Error)); 
     187                    Logger.Log(S._("{0} did not have its cluster tips erased. The error returned " + 
     188                        "was: {1}", files[i], e.Message), LogLevel.Error); 
    191189                } 
    192190                finally 
     
    194192                    streamInfo.Attributes = fileAttr; 
    195193                } 
     194 
    196195                eraseCallback(i, files.Count, files[i]); 
    197196            } 
    198197        } 
    199198 
    200         private void ListFiles(DirectoryInfo info, List<string> files, Logger log, 
     199        private void ListFiles(DirectoryInfo info, List<string> files, 
    201200            ClusterTipsSearchProgress searchCallback) 
    202201        { 
     
    206205                if ((info.Attributes & FileAttributes.ReparsePoint) != 0) 
    207206                { 
    208                     log.LastSessionEntries.Add(new LogEntry(S._("Files in {0} did " + 
    209                         "not have their cluster tips erased because it is a hard link or " + 
    210                         "a symbolic link.", info.FullName), LogLevel.Information)); 
     207                    Logger.Log(S._("Files in {0} did not have their cluster tips erased because " + 
     208                        "it is a hard link or a symbolic link.", info.FullName), 
     209                        LogLevel.Information); 
    211210                    return; 
    212211                } 
     
    214213                foreach (FileInfo file in info.GetFiles()) 
    215214                    if (file.IsProtectedSystemFile()) 
    216                         log.LastSessionEntries.Add(new LogEntry(S._("{0} did not have " + 
    217                             "its cluster tips erased, because it is a system file", 
    218                             file.FullName), LogLevel.Information)); 
     215                        Logger.Log(S._("{0} did not have its cluster tips erased, because it is " + 
     216                            "a system file", file.FullName), LogLevel.Information); 
    219217                    else if ((file.Attributes & FileAttributes.ReparsePoint) != 0) 
    220                         log.LastSessionEntries.Add(new LogEntry(S._("{0} did not have " + 
    221                             "its cluster tips erased because it is a hard link or a " + 
    222                             "symbolic link.", file.FullName), LogLevel.Information)); 
     218                        Logger.Log(S._("{0} did not have its cluster tips erased because it is a " + 
     219                            "hard link or a symbolic link.", file.FullName), LogLevel.Information); 
    223220                    else if ((file.Attributes & FileAttributes.Compressed) != 0 || 
    224221                        (file.Attributes & FileAttributes.Encrypted) != 0 || 
    225222                        (file.Attributes & FileAttributes.SparseFile) != 0) 
    226223                    { 
    227                         log.LastSessionEntries.Add(new LogEntry(S._("{0} did not have " + 
    228                             "its cluster tips erased because it is compressed, encrypted " + 
    229                             "or a sparse file.", file.FullName), LogLevel.Information)); 
     224                        Logger.Log(S._("{0} did not have its cluster tips erased because it is " + 
     225                            "compressed, encrypted or a sparse file.", file.FullName), 
     226                            LogLevel.Information); 
    230227                    } 
    231228                    else 
     
    240237                        catch (UnauthorizedAccessException e) 
    241238                        { 
    242                             log.LastSessionEntries.Add(new LogEntry(S._("{0} did not " + 
    243                                 "have its cluster tips erased because of the following " + 
    244                                 "error: {1}", info.FullName, e.Message), LogLevel.Error)); 
     239                            Logger.Log(S._("{0} did not have its cluster tips erased because of " + 
     240                                "the following error: {1}", info.FullName, e.Message), 
     241                                LogLevel.Error); 
    245242                        } 
    246243                        catch (IOException e) 
    247244                        { 
    248                             log.LastSessionEntries.Add(new LogEntry(S._("{0} did not " + 
    249                                 "have its cluster tips erased because of the following " + 
    250                                 "error: {1}", info.FullName, e.Message), LogLevel.Error)); 
     245                            Logger.Log(S._("{0} did not have its cluster tips erased because of " + 
     246                                "the following error: {1}", info.FullName, e.Message), 
     247                                LogLevel.Error); 
    251248                        } 
    252249                    } 
     
    255252                { 
    256253                    searchCallback(subDirInfo.FullName); 
    257                     ListFiles(subDirInfo, files, log, searchCallback); 
     254                    ListFiles(subDirInfo, files, searchCallback); 
    258255                } 
    259256            } 
    260257            catch (UnauthorizedAccessException e) 
    261258            { 
    262                 log.LastSessionEntries.Add(new LogEntry(S._("{0} did not have its " + 
    263                     "cluster tips erased because of the following error: {1}", 
    264                     info.FullName, e.Message), LogLevel.Error)); 
     259                Logger.Log(S._("{0} did not have its cluster tips erased because of the " + 
     260                    "following error: {1}", info.FullName, e.Message), LogLevel.Error); 
    265261            } 
    266262            catch (IOException e) 
    267263            { 
    268                 log.LastSessionEntries.Add(new LogEntry(S._("{0} did not have its " + 
    269                     "cluster tips erased because of the following error: {1}", 
    270                     info.FullName, e.Message), LogLevel.Error)); 
     264                Logger.Log(S._("{0} did not have its cluster tips erased because of the " + 
     265                    "following error: {1}", info.FullName, e.Message), LogLevel.Error); 
    271266            } 
    272267        } 
  • branches/eraser6/CodeReview/Eraser.Manager/DirectExecutor.cs

    r1772 r1777  
    255255                    currentTask = task; 
    256256 
    257                         //Prevent the system from sleeping. 
    258                         Power.ExecutionState = ExecutionState.Continuous | 
    259                             ExecutionState.SystemRequired; 
     257                    //Prevent the system from sleeping. 
     258                    Power.ExecutionState = ExecutionState.Continuous | ExecutionState.SystemRequired; 
    260259 
    261260                    //Start a new log session to separate this session's events 
    262261                    //from previous ones. 
    263                     task.Log.Entries.NewSession(); 
    264  
    265                     try 
    266                     { 
    267                         //Broadcast the task started event. 
    268                         task.Canceled = false; 
    269                         task.OnTaskStarted(); 
    270  
    271                         //Run the task 
    272                         foreach (ErasureTarget target in task.Targets) 
    273                             try 
    274                             { 
    275                                 UnusedSpaceTarget unusedSpaceTarget = 
    276                                     target as UnusedSpaceTarget; 
    277                                 FileSystemObjectTarget fileSystemObjectTarget = 
    278                                     target as FileSystemObjectTarget; 
    279  
    280                                 if (unusedSpaceTarget != null) 
    281                                     EraseUnusedSpace(task, unusedSpaceTarget); 
    282                                 else if (fileSystemObjectTarget != null) 
    283                                     EraseFilesystemObject(task, fileSystemObjectTarget); 
    284                                 else 
    285                                     throw new ArgumentException("Unknown erasure target."); 
    286                             } 
    287                             catch (FatalException) 
    288                             { 
    289                                 throw; 
    290                             } 
    291                             catch (OperationCanceledException) 
    292                             { 
    293                                 throw; 
    294                             } 
    295                             catch (ThreadAbortException) 
    296                             { 
    297                             } 
    298                             catch (Exception e) 
    299                             { 
    300                                 task.Log.LastSessionEntries.Add(new LogEntry(e.Message, LogLevel.Error)); 
    301                                 BlackBox.Get().CreateReport(e); 
    302                             } 
    303                     } 
    304                     catch (FatalException e) 
    305                     { 
    306                         task.Log.LastSessionEntries.Add(new LogEntry(e.Message, LogLevel.Fatal)); 
    307                     } 
    308                     catch (OperationCanceledException e) 
    309                     { 
    310                         task.Log.LastSessionEntries.Add(new LogEntry(e.Message, LogLevel.Fatal)); 
    311                     } 
    312                     catch (ThreadAbortException) 
    313                     { 
    314                         //Do nothing. The exception will be rethrown after this block 
    315                         //is executed. This is here mainly to ensure that no BlackBox 
    316                         //report is created for this exception. 
    317                     } 
    318                     catch (Exception e) 
    319                     { 
    320                         task.Log.LastSessionEntries.Add(new LogEntry(e.Message, LogLevel.Error)); 
    321                         BlackBox.Get().CreateReport(e); 
    322                     } 
    323                     finally 
    324                     { 
    325                         //Allow the system to sleep again. 
    326                         Power.ExecutionState = ExecutionState.Continuous; 
    327  
    328                         //If the task is a recurring task, reschedule it since we are done. 
    329                         if (task.Schedule is RecurringSchedule) 
    330                             ((RecurringSchedule)task.Schedule).Reschedule(DateTime.Now); 
    331  
    332                         //If the task is an execute on restart task, it is only run 
    333                         //once and can now be restored to an immediately executed task 
    334                         if (task.Schedule == Schedule.RunOnRestart) 
    335                             task.Schedule = Schedule.RunNow; 
    336  
    337                         //And the task finished event. 
    338                         task.OnTaskFinished(); 
    339  
    340                         //Remove the actively executing task from our instance variable 
    341                         currentTask = null; 
     262                    LogSink sessionLog = new LogSink(); 
     263                    task.Log.Add(sessionLog); 
     264                    using (new LogSession(sessionLog)) 
     265                    { 
     266                        ExecuteTask(task); 
    342267                    } 
    343268                } 
     
    345270                //Wait for half a minute to check for the next scheduled task. 
    346271                schedulerInterrupt.WaitOne(30000, false); 
     272            } 
     273        } 
     274 
     275        /// <summary> 
     276        /// Executes the given task. 
     277        /// </summary> 
     278        /// <param name="task">The task to execute.</param> 
     279        private void ExecuteTask(Task task) 
     280        { 
     281            try 
     282            { 
     283                //Broadcast the task started event. 
     284                task.Canceled = false; 
     285                task.OnTaskStarted(); 
     286 
     287                //Run the task 
     288                foreach (ErasureTarget target in task.Targets) 
     289                    try 
     290                    { 
     291                        UnusedSpaceTarget unusedSpaceTarget = 
     292                            target as UnusedSpaceTarget; 
     293                        FileSystemObjectTarget fileSystemObjectTarget = 
     294                            target as FileSystemObjectTarget; 
     295 
     296                        if (unusedSpaceTarget != null) 
     297                            EraseUnusedSpace(task, unusedSpaceTarget); 
     298                        else if (fileSystemObjectTarget != null) 
     299                            EraseFilesystemObject(task, fileSystemObjectTarget); 
     300                        else 
     301                            throw new ArgumentException("Unknown erasure target."); 
     302                    } 
     303                    catch (FatalException) 
     304                    { 
     305                        throw; 
     306                    } 
     307                    catch (OperationCanceledException) 
     308                    { 
     309                        throw; 
     310                    } 
     311                    catch (ThreadAbortException) 
     312                    { 
     313                    } 
     314                    catch (Exception e) 
     315                    { 
     316                        Logger.Log(e.Message, LogLevel.Error); 
     317                        BlackBox.Get().CreateReport(e); 
     318                    } 
     319            } 
     320            catch (FatalException e) 
     321            { 
     322                Logger.Log(e.Message, LogLevel.Fatal); 
     323            } 
     324            catch (OperationCanceledException e) 
     325            { 
     326                Logger.Log(e.Message, LogLevel.Fatal); 
     327            } 
     328            catch (ThreadAbortException) 
     329            { 
     330                //Do nothing. The exception will be rethrown after this block 
     331                //is executed. This is here mainly to ensure that no BlackBox 
     332                //report is created for this exception. 
     333            } 
     334            catch (Exception e) 
     335            { 
     336                Logger.Log(e.Message, LogLevel.Error); 
     337                BlackBox.Get().CreateReport(e); 
     338            } 
     339            finally 
     340            { 
     341                //Allow the system to sleep again. 
     342                Power.ExecutionState = ExecutionState.Continuous; 
     343 
     344                //If the task is a recurring task, reschedule it since we are done. 
     345                if (task.Schedule is RecurringSchedule) 
     346                    ((RecurringSchedule)task.Schedule).Reschedule(DateTime.Now); 
     347 
     348                //If the task is an execute on restart task, it is only run 
     349                //once and can now be restored to an immediately executed task 
     350                if (task.Schedule == Schedule.RunOnRestart) 
     351                    task.Schedule = Schedule.RunNow; 
     352 
     353                //And the task finished event. 
     354                task.OnTaskFinished(); 
     355 
     356                //Remove the actively executing task from our instance variable 
     357                currentTask = null; 
    347358            } 
    348359        } 
     
    361372                    Environment.OSVersion.Version >= new Version(6, 0)) 
    362373                { 
    363                     task.Log.LastSessionEntries.Add(new LogEntry(S._("The program does not have " + 
    364                         "the required permissions to erase the unused space on disk. Run the " + 
    365                         "program as an administrator and retry the operation."), LogLevel.Error)); 
     374                    Logger.Log(S._("The program does not have the required permissions to erase " + 
     375                        "the unused space on disk. Run the program as an administrator and retry " + 
     376                        "the operation."), LogLevel.Error); 
    366377                } 
    367378                else 
    368379                { 
    369                     task.Log.LastSessionEntries.Add(new LogEntry(S._("The program does not have " + 
    370                         "the required permissions to erase the unused space on disk."), 
    371                         LogLevel.Error)); 
     380                    Logger.Log(S._("The program does not have the required permissions to erase " + 
     381                        "the unused space on disk."), LogLevel.Error); 
    372382                } 
    373383            } 
     
    376386            if (SystemRestore.GetInstances().Count != 0) 
    377387            { 
    378                 task.Log.LastSessionEntries.Add(new LogEntry(S._("The drive {0} has System " + 
    379                     "Restore or Volume Shadow Copies enabled. This may allow copies of files " + 
    380                     "stored on the disk to be recovered and pose a security concern.", 
    381                     target.Drive), LogLevel.Warning)); 
     388                Logger.Log(S._("The drive {0} has System Restore or Volume Shadow Copies " + 
     389                    "enabled. This may allow copies of files stored on the disk to be recovered " + 
     390                    "and pose a security concern.", target.Drive), LogLevel.Warning); 
    382391            } 
    383392             
    384393            //If the user is under disk quotas, log a warning message 
    385394            if (VolumeInfo.FromMountPoint(target.Drive).HasQuota) 
    386                 task.Log.LastSessionEntries.Add(new LogEntry(S._("The drive {0} has disk quotas " + 
    387                     "active. This will prevent the complete erasure of unused space and may pose " + 
    388                     "a security concern.", target.Drive), LogLevel.Warning)); 
     395                Logger.Log(S._("The drive {0} has disk quotas active. This will prevent the " + 
     396                    "complete erasure of unused space and may pose a security concern.", 
     397                    target.Drive), LogLevel.Warning); 
    389398 
    390399            //Get the erasure method if the user specified he wants the default. 
     
    439448                //Start counting statistics 
    440449                fsManager.EraseClusterTips(VolumeInfo.FromMountPoint(target.Drive), 
    441                     method, task.Log, searchProgress, eraseProgress); 
     450                    method, searchProgress, eraseProgress); 
    442451                tipProgress.MarkComplete(); 
    443452            } 
     
    608617                if (!info.Exists) 
    609618                { 
    610                     task.Log.LastSessionEntries.Add(new LogEntry(S._("The file {0} was not erased " + 
    611                         "as the file does not exist.", paths[i]), LogLevel.Notice)); 
     619                    Logger.Log(S._("The file {0} was not erased as the file does not exist.", 
     620                        paths[i]), LogLevel.Notice); 
    612621                    continue; 
    613622                } 
     
    632641                    { 
    633642                        //Log the error 
    634                         task.Log.LastSessionEntries.Add(new LogEntry(S._("The file {0} could " + 
    635                             "not be erased because the file was either compressed, encrypted or " + 
    636                             "a sparse file.", info.FullName), LogLevel.Error)); 
     643                        Logger.Log(S._("The file {0} could not be erased because the file was " + 
     644                            "either compressed, encrypted or a sparse file.", info.FullName), 
     645                            LogLevel.Error); 
    637646                        continue; 
    638647                    } 
     
    659668                catch (UnauthorizedAccessException) 
    660669                { 
    661                     task.Log.LastSessionEntries.Add(new LogEntry(S._("The file {0} could not " + 
    662                         "be erased because the file's permissions prevent access to the file.", 
    663                         info.FullName), LogLevel.Error)); 
     670                    Logger.Log(S._("The file {0} could not be erased because the file's " + 
     671                        "permissions prevent access to the file.", info.FullName), LogLevel.Error); 
    664672                } 
    665673                catch (IOException) 
     
    696704                        } 
    697705 
    698                         task.Log.LastSessionEntries.Add(new LogEntry(S._( 
    699                             "Could not force closure of file \"{0}\" {1}", paths[i], 
    700                             lockedBy == null ? string.Empty : lockedBy).Trim(), LogLevel.Error)); 
     706                        Logger.Log(S._("Could not force closure of file \"{0}\" {1}", paths[i], 
     707                            lockedBy == null ? string.Empty : lockedBy).Trim(), LogLevel.Error); 
    701708                    } 
    702709                    else 
  • branches/eraser6/CodeReview/Eraser.Manager/Eraser.Manager.csproj

    r1625 r1777  
    7171    <Compile Include="FileSystem.cs" /> 
    7272    <Compile Include="Language.cs" /> 
    73     <Compile Include="Logger.cs" /> 
    7473    <Compile Include="ManagerLibrary.cs" /> 
    7574    <Compile Include="Method.cs" /> 
  • branches/eraser6/CodeReview/Eraser.Manager/FileSystem.cs

    r1746 r1777  
    203203        /// <param name="eraseCallback">The callback function for erasure progress.</param> 
    204204        public abstract void EraseClusterTips(VolumeInfo info, ErasureMethod method, 
    205             Logger log, ClusterTipsSearchProgress searchCallback, 
    206             ClusterTipsEraseProgress eraseCallback); 
     205            ClusterTipsSearchProgress searchCallback, ClusterTipsEraseProgress eraseCallback); 
    207206 
    208207        /// <summary> 
  • branches/eraser6/CodeReview/Eraser.Manager/Task.cs

    r1767 r1777  
    4747            Targets = (ErasureTargetsCollection)info.GetValue("Targets", typeof(ErasureTargetsCollection)); 
    4848            Targets.Owner = this; 
    49             Log = (Logger)info.GetValue("Log", typeof(Logger)); 
     49            Log = (List<LogSink>)info.GetValue("Log", typeof(List<LogSink>)); 
    5050            Canceled = false; 
    5151 
     
    8383            Schedule = Schedule.RunNow; 
    8484            Canceled = false; 
    85             Log = new Logger(); 
     85            Log = new List<LogSink>(); 
    8686        } 
    8787 
     
    198198        /// The log entries which this task has accumulated. 
    199199        /// </summary> 
    200         public Logger Log { get; private set; } 
     200        public List<LogSink> Log { get; private set; } 
    201201 
    202202        /// <summary> 
     
    481481                //The system cannot read the file, assume no ADSes for lack of 
    482482                //more information. 
    483                 Task.Log.LastSessionEntries.Add(new LogEntry(e.Message, LogLevel.Error)); 
     483                Logger.Log(e.Message, LogLevel.Error); 
    484484            } 
    485485        } 
     
    743743                catch (UnauthorizedAccessException e) 
    744744                { 
    745                     Task.Log.LastSessionEntries.Add(new LogEntry(S._("Could not erase files and " + 
    746                         "subfolders in {0} because {1}", info.FullName, e.Message), LogLevel.Error)); 
     745                    Logger.Log(S._("Could not erase files and subfolders in {0} because {1}", 
     746                        info.FullName, e.Message), LogLevel.Error); 
    747747                } 
    748748            } 
     
    840840            catch (UnauthorizedAccessException e) 
    841841            { 
    842                 Task.Log.LastSessionEntries.Add(new LogEntry(e.Message, LogLevel.Error)); 
     842                Logger.Log(e.Message, LogLevel.Error); 
    843843            } 
    844844        } 
  • branches/eraser6/CodeReview/Eraser.Util/Eraser.Util.csproj

    r1745 r1777  
    5050  </PropertyGroup> 
    5151  <ItemGroup> 
     52    <Reference Include="CommonLibrary, Version=0.9.3.10, Culture=neutral, PublicKeyToken=3ac89a0351e689b6, processorArchitecture=MSIL"> 
     53      <SpecificVersion>False</SpecificVersion> 
     54      <HintPath>..\Dependencies\CommonLibrary.dll</HintPath> 
     55    </Reference> 
    5256    <Reference Include="ICSharpCode.SharpZipLib, Version=0.85.5.452, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL"> 
    5357      <SpecificVersion>False</SpecificVersion> 
     
    102106    <Compile Include="ConsoleWindow.cs" /> 
    103107    <Compile Include="FileSize.cs" /> 
     108    <Compile Include="Logger.cs" /> 
    104109    <Compile Include="NativeMethods\DbgHelp.cs" /> 
    105110    <Compile Include="NativeMethods\Mpr.cs" /> 
  • branches/eraser6/CodeReview/Eraser.Util/Logger.cs

    r1772 r1777  
    1 /*  
     1/*  
    22 * $Id$ 
    33 * Copyright 2008-2010 The Eraser Project 
     
    2121 
    2222using System; 
     23using System.Linq; 
    2324using System.Collections.Generic; 
    2425using System.Text; 
     26 
     27using System.Collections.ObjectModel; 
     28using System.Threading; 
    2529using System.Runtime.Serialization; 
    2630using System.Security.Permissions; 
    2731 
    28 namespace Eraser.Manager 
     32namespace Eraser.Util 
    2933{ 
    3034    /// <summary> 
     
    6064 
    6165    /// <summary> 
    62     /// The Logger class which handles log entries and manages entries. 
    63     ///  
    64     /// The class has the notion of entries and sessions. Each session contains one 
    65     /// or more (log) entries. This allows the program to determine if the last 
    66     /// session had errors or not. 
    67     /// </summary> 
    68     [Serializable] 
    69     public class Logger : ISerializable 
    70     { 
    71         #region Serialization code 
    72         protected Logger(SerializationInfo info, StreamingContext context) 
    73         { 
    74             Entries = (LogSessionDictionary)info.GetValue("Entries", typeof(LogSessionDictionary)); 
    75             Entries.Owner = this; 
    76             foreach (DateTime key in Entries.Keys) 
    77                 LastSession = key; 
    78         } 
    79  
    80         [SecurityPermission(SecurityAction.Demand, SerializationFormatter=true)] 
    81         public virtual void GetObjectData(SerializationInfo info, StreamingContext context) 
    82         { 
    83             info.AddValue("Entries", Entries); 
    84         } 
    85         #endregion 
    86  
    87         /// <summary> 
    88         /// Constructor. 
    89         /// </summary> 
    90         public Logger() 
    91         { 
    92             Entries = new LogSessionDictionary(this); 
    93         } 
    94  
    95         /// <summary> 
    96         /// All the registered event handlers for the log event of this task. 
    97         /// </summary> 
    98         public EventHandler<LogEventArgs> Logged { get; set; } 
    99  
    100         internal void OnLogged(object sender, LogEventArgs e) 
    101         { 
    102             if (Logged != null) 
    103                 Logged(sender, e); 
    104         } 
    105  
    106         /// <summary> 
    107         /// All the registered event handlers for handling when a new session has been 
    108         /// started. 
    109         /// </summary> 
    110         public EventHandler<EventArgs> NewSession { get; set; } 
    111  
    112         internal void OnNewSession(object sender, EventArgs e) 
    113         { 
    114             if (NewSession != null) 
    115                 NewSession(sender, e); 
    116         } 
    117  
    118         /// <summary> 
    119         /// Retrieves the log for this task. 
    120         /// </summary> 
    121         public LogSessionDictionary Entries { get; private set; } 
    122  
    123         /// <summary> 
    124         /// Retrieves the log entries from the previous session. 
    125         /// </summary> 
    126         public LogEntryCollection LastSessionEntries 
    127         { 
    128             get 
    129             { 
    130                 return Entries[LastSession]; 
    131             } 
    132         } 
    133  
    134         /// <summary> 
    135         /// Clears the log entries from the log. 
    136         /// </summary> 
    137         public void Clear() 
    138         { 
    139             LogEntryCollection lastSessionEntries = null; 
    140             if (Entries.ContainsKey(LastSession)) 
    141                 lastSessionEntries = Entries[LastSession]; 
    142             Entries.Clear(); 
    143  
    144             if (lastSessionEntries != null) 
    145                 Entries.Add(LastSession, lastSessionEntries); 
    146         } 
    147  
    148         /// <summary> 
    149         /// The date and time of the last session. 
    150         /// </summary> 
    151         public DateTime LastSession 
    152         { 
    153             get { return lastSession; } 
    154             internal set { lastSession = value; OnNewSession(null, EventArgs.Empty); } 
    155         } 
    156  
    157         private DateTime lastSession; 
    158     } 
    159  
    160     public class LogEventArgs : EventArgs 
    161     { 
    162         /// <summary> 
    163         /// Constructor. 
    164         /// </summary> 
    165         /// <param name="entry">The log entry that was just logged.</param> 
    166         public LogEventArgs(LogEntry entry) 
    167         { 
    168             LogEntry = entry; 
    169         } 
    170  
    171         /// <summary> 
    172         /// The log entry that was just logged. 
    173         /// </summary> 
    174         public LogEntry LogEntry { get; private set; } 
    175     } 
    176  
    177     [Serializable] 
    178     public class LogSessionDictionary : IDictionary<DateTime, LogEntryCollection>, 
    179         ISerializable 
    180     { 
    181         /// <summary> 
    182         /// Constructor. 
    183         /// </summary> 
    184         /// <param name="logger">The logger object managing the logging.</param> 
    185         public LogSessionDictionary(Logger logger) 
    186         { 
    187             Owner = logger; 
    188         } 
    189  
    190         public void NewSession() 
    191         { 
    192             DateTime sessionTime = DateTime.Now; 
    193             Add(sessionTime, new LogEntryCollection(Owner)); 
    194             Owner.LastSession = sessionTime; 
    195         } 
    196  
    197         #region ISerializable Members 
    198         protected LogSessionDictionary(SerializationInfo info, StreamingContext context) 
    199         { 
    200             dictionary = (Dictionary<DateTime, LogEntryCollection>)info.GetValue("Dictionary", 
    201                 dictionary.GetType()); 
    202         } 
    203  
    204         [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] 
    205         public virtual void GetObjectData(SerializationInfo info, StreamingContext context) 
    206         { 
    207             lock (dictionary) 
    208                 info.AddValue("Dictionary", dictionary); 
    209         } 
    210         #endregion 
    211  
    212         #region IDictionary<DateTime,LogSessionEntryCollection> Members 
    213         public void Add(DateTime key, LogEntryCollection value) 
    214         { 
    215             lock (dictionary) 
    216                 dictionary.Add(key, value); 
    217         } 
    218  
    219         public bool ContainsKey(DateTime key) 
    220         { 
    221             lock (dictionary) 
    222                 return dictionary.ContainsKey(key); 
    223         } 
    224  
    225         public ICollection<DateTime> Keys 
    226         { 
    227             get 
    228             { 
    229                 lock (dictionary) 
    230                 { 
    231                     DateTime[] result = new DateTime[dictionary.Keys.Count]; 
    232                     dictionary.Keys.CopyTo(result, 0); 
    233                     return result; 
    234                 } 
    235             } 
    236         } 
    237  
    238         public bool Remove(DateTime key) 
    239         { 
    240             lock (dictionary) 
    241                 return dictionary.Remove(key); 
    242         } 
    243  
    244         public bool TryGetValue(DateTime key, out LogEntryCollection value) 
    245         { 
    246             lock (dictionary) 
    247                 return dictionary.TryGetValue(key, out value); 
    248         } 
    249  
    250         public ICollection<LogEntryCollection> Values 
    251         { 
    252             get 
    253             { 
    254                 lock (dictionary) 
    255                 { 
    256                     LogEntryCollection[] result = new LogEntryCollection[dictionary.Values.Count]; 
    257                     dictionary.Values.CopyTo(result, 0); 
    258                     return result; 
    259                 } 
    260             } 
    261         } 
    262  
    263         public LogEntryCollection this[DateTime key] 
    264         { 
    265             get 
    266             { 
    267                 lock (dictionary) 
    268                     return dictionary[key]; 
    269             } 
    270             set 
    271             { 
    272                 lock (dictionary) 
    273                     dictionary[key] = value; 
    274             } 
    275         } 
    276         #endregion 
    277  
    278         #region ICollection<KeyValuePair<DateTime,LogSessionEntryCollection>> Members 
    279         public void Add(KeyValuePair<DateTime, LogEntryCollection> item) 
    280         { 
    281             Add(item.Key, item.Value); 
    282         } 
    283  
    284         public void Clear() 
    285         { 
    286             lock (dictionary) 
    287                 dictionary.Clear(); 
    288         } 
    289  
    290         public bool Contains(KeyValuePair<DateTime, LogEntryCollection> item) 
    291         { 
    292             lock (dictionary) 
    293                 return dictionary.ContainsKey(item.Key) && dictionary[item.Key] == item.Value; 
    294         } 
    295  
    296         public void CopyTo(KeyValuePair<DateTime, LogEntryCollection>[] array, int arrayIndex) 
    297         { 
    298             throw new NotImplementedException(); 
    299         } 
    300  
    301         public int Count 
    302         { 
    303             get 
    304             { 
    305                 lock (dictionary) 
    306                     return dictionary.Count; 
    307             } 
    308         } 
    309  
    310         public bool IsReadOnly 
    311         { 
    312             get { return false; } 
    313         } 
    314  
    315         public bool Remove(KeyValuePair<DateTime, LogEntryCollection> item) 
    316         { 
    317             lock (dictionary) 
    318                 return dictionary.Remove(item.Key); 
    319         } 
    320         #endregion 
    321  
    322         #region IEnumerable<KeyValuePair<DateTime,LogSessionEntryCollection>> Members 
    323         public IEnumerator<KeyValuePair<DateTime, LogEntryCollection>> GetEnumerator() 
    324         { 
    325             return dictionary.GetEnumerator(); 
    326         } 
    327         #endregion 
    328  
    329         #region IEnumerable Members 
    330         System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
    331         { 
    332             return GetEnumerator(); 
    333         } 
    334         #endregion 
    335  
    336         /// <summary> 
    337         /// The log manager. 
    338         /// </summary> 
    339         internal Logger Owner 
    340         { 
    341             get 
    342             { 
    343                 return owner; 
    344             } 
    345             set 
    346             { 
    347                 lock (dictionary) 
    348                     foreach (LogEntryCollection entries in dictionary.Values) 
    349                         entries.owner = value; 
    350                 owner = value; 
    351             } 
    352         } 
    353  
    354         /// <summary> 
    355         /// The log manager. 
    356         /// </summary> 
    357         private Logger owner; 
    358  
    359         /// <summary> 
    360         /// The store for this object. 
    361         /// </summary> 
    362         private Dictionary<DateTime, LogEntryCollection> dictionary = 
    363             new Dictionary<DateTime, LogEntryCollection>(); 
    364     } 
    365  
    366     [Serializable] 
    367     public class LogEntryCollection : IList<LogEntry> 
    368     { 
    369         /// <summary> 
    370         /// Constructor. 
    371         /// </summary> 
    372         /// <param name="logger">The <see cref="Logger"/> object handling logging.</param> 
    373         internal LogEntryCollection(Logger logger) 
    374         { 
    375             owner = logger; 
    376         } 
    377  
    378         #region IList<LogEntry> Members 
    379         public int IndexOf(LogEntry item) 
    380         { 
    381             lock (list) 
    382                 return list.IndexOf(item); 
    383         } 
    384  
    385         public void Insert(int index, LogEntry item) 
    386         { 
    387             lock (list) 
    388                 list.Insert(index, item); 
    389             owner.OnLogged(owner, new LogEventArgs(item)); 
    390         } 
    391  
    392         public void RemoveAt(int index) 
    393         { 
    394             throw new InvalidOperationException(); 
    395         } 
    396  
    397         public LogEntry this[int index] 
    398         { 
    399             get 
    400             { 
    401                 lock (list) 
    402                     return list[index]; 
    403             } 
    404             set 
    405             { 
    406                 throw new InvalidOperationException(); 
    407             } 
    408         } 
    409         #endregion 
    410  
    411         #region ICollection<LogEntry> Members 
    412         public void Add(LogEntry item) 
    413         { 
    414             Insert(Count, item); 
    415         } 
    416  
    417         public void Clear() 
    418         { 
    419             throw new InvalidOperationException(); 
    420         } 
    421  
    422         public bool Contains(LogEntry item) 
    423         { 
    424             lock (list) 
    425                 return list.Contains(item); 
    426         } 
    427  
    428         public void CopyTo(LogEntry[] array, int arrayIndex) 
    429         { 
    430             lock (list) 
    431                 list.CopyTo(array, arrayIndex); 
    432         } 
    433  
    434         public int Count 
    435         { 
    436             get 
    437             { 
    438                 lock (list) 
    439                     return list.Count; 
    440             } 
    441         } 
    442  
    443         public bool IsReadOnly 
    444         { 
    445             get { return false; } 
    446         } 
    447  
    448         public bool Remove(LogEntry item) 
    449         { 
    450             lock (list) 
    451                 return list.Remove(item); 
    452         } 
    453         #endregion 
    454  
    455         #region IEnumerable<LogEntry> Members 
    456         public IEnumerator<LogEntry> GetEnumerator() 
    457         { 
    458             return list.GetEnumerator(); 
    459         } 
    460         #endregion 
    461  
    462         #region IEnumerable Members 
    463         System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
    464         { 
    465             return GetEnumerator(); 
    466         } 
    467         #endregion 
    468  
    469         /// <summary> 
    470         /// The Logger object managing logging. 
    471         /// </summary> 
    472         internal Logger owner; 
    473  
    474         /// <summary> 
    475         /// The store for this object. 
    476         /// </summary> 
    477         private List<LogEntry> list = new List<LogEntry>(); 
    478     } 
    479  
    480     /// <summary> 
    48166    /// Represents a log entry. 
    48267    /// </summary> 
     
    530115        public string Message { get; private set; } 
    531116    } 
     117 
     118    /// <summary> 
     119    /// Event Data for all Logger events. 
     120    /// </summary> 
     121    public class LogEventArgs : EventArgs 
     122    { 
     123        /// <summary> 
     124        /// Constructor. 
     125        /// </summary> 
     126        /// <param name="entry">The log entry that was just logged.</param> 
     127        public LogEventArgs(LogEntry entry) 
     128        { 
     129            LogEntry = entry; 
     130        } 
     131 
     132        /// <summary> 
     133        /// The log entry that was just logged. 
     134        /// </summary> 
     135        public LogEntry LogEntry { get; private set; } 
     136    } 
     137 
     138    /// <summary> 
     139    /// Provides a standard logging interface to the rest of the Eraser classes. 
     140    /// </summary> 
     141    public static class Logger 
     142    { 
     143        static Logger() 
     144        { 
     145            Listeners = new LogThreadDictionary(); 
     146        } 
     147 
     148        /// <summary> 
     149        /// Logs an informational message. 
     150        /// </summary> 
     151        /// <param name="message">The message to log.</param> 
     152        public static void Log(string message) 
     153        { 
     154            Log(new LogEntry(message, LogLevel.Information)); 
     155        } 
     156 
     157        /// <summary> 
     158        /// Logs a message to the logger. 
     159        /// </summary> 
     160        /// <param name="message">The message to store.</param> 
     161        /// <param name="level">The level of the message.</param> 
     162        public static void Log(string message, LogLevel level) 
     163        { 
     164            Log(new LogEntry(message, level)); 
     165        } 
     166 
     167        /// <summary> 
     168        /// Logs the provided entry to the logger. 
     169        /// </summary> 
     170        /// <param name="entry">The log entry to store.</param> 
     171        public static void Log(LogEntry entry) 
     172        { 
     173            Thread currentThread = Thread.CurrentThread; 
     174            List<ILogTarget> targets = new List<ILogTarget>(); 
     175 
     176            if (Listeners.ContainsKey(currentThread)) 
     177            { 
     178                LogThreadTargets threadTargets = Listeners[currentThread]; 
     179                if (threadTargets != null) 
     180                    targets.AddRange(threadTargets); 
     181            } 
     182 
     183            targets.ForEach( 
     184                target => target.OnEventLogged(currentThread, new LogEventArgs(entry))); 
     185        } 
     186 
     187        /// <summary> 
     188        /// The list of listeners for events on a particular thread. 
     189        /// </summary> 
     190        public static LogThreadDictionary Listeners { get; private set; } 
     191    } 
     192 
     193    /// <summary> 
     194    /// The Logger Thread Dictionary, which maps log event listeners to threads. 
     195    /// This mainly serves as a thread-safe Dictionary. 
     196    /// </summary> 
     197    public class LogThreadDictionary : IDictionary<Thread, LogThreadTargets> 
     198    { 
     199        #region IDictionary<Thread,LogThreadTargets> Members 
     200 
     201        public void Add(Thread key, LogThreadTargets value) 
     202        { 
     203            lock (Dictionary) 
     204                Dictionary.Add(key, value); 
     205        } 
     206 
     207        public bool ContainsKey(Thread key) 
     208        { 
     209            return Dictionary.ContainsKey(key); 
     210        } 
     211 
     212        public ICollection<Thread> Keys 
     213        { 
     214            get 
     215            { 
     216                lock (Dictionary) 
     217                { 
     218                    Thread[] result = new Thread[Dictionary.Keys.Count]; 
     219                    Dictionary.Keys.CopyTo(result, 0); 
     220 
     221                    return new ReadOnlyCollection<Thread>(result); 
     222                } 
     223            } 
     224        } 
     225 
     226        public bool Remove(Thread key) 
     227        { 
     228            lock (Dictionary) 
     229                return Dictionary.Remove(key); 
     230        } 
     231 
     232        public bool TryGetValue(Thread key, out LogThreadTargets value) 
     233        { 
     234            lock (Dictionary) 
     235                return Dictionary.TryGetValue(key, out value); 
     236        } 
     237 
     238        public ICollection<LogThreadTargets> Values 
     239        { 
     240            get 
     241            { 
     242                lock (Dictionary) 
     243                { 
     244                    LogThreadTargets[] result = 
     245                        new LogThreadTargets[Dictionary.Values.Count]; 
     246                    Dictionary.Values.CopyTo(result, 0); 
     247 
     248                    return new ReadOnlyCollection<LogThreadTargets>(result); 
     249                } 
     250            } 
     251        } 
     252 
     253        public LogThreadTargets this[Thread key] 
     254        { 
     255            get 
     256            { 
     257                lock (Dictionary) 
     258                    return Dictionary[key]; 
     259            } 
     260            set 
     261            { 
     262                lock (Dictionary) 
     263                    Dictionary[key] = value; 
     264            } 
     265        } 
     266 
     267        #endregion 
     268 
     269        #region ICollection<KeyValuePair<Thread,LogThreadTargets>> Members 
     270 
     271        public void Add(KeyValuePair<Thread, LogThreadTargets> item) 
     272        { 
     273            lock (Dictionary) 
     274                Dictionary.Add(item.Key, item.Value); 
     275        } 
     276 
     277        public void Clear() 
     278        { 
     279            lock (Dictionary) 
     280                Dictionary.Clear(); 
     281        } 
     282 
     283        public bool Contains(KeyValuePair<Thread, LogThreadTargets> item) 
     284        { 
     285            lock (Dictionary) 
     286                return Dictionary.ContainsKey(item.Key) && Dictionary[item.Key] == item.Value; 
     287        } 
     288 
     289        public void CopyTo(KeyValuePair<Thread, LogThreadTargets>[] array, int arrayIndex) 
     290        { 
     291            throw new NotImplementedException(); 
     292        } 
     293 
     294        public int Count 
     295        { 
     296            get 
     297            { 
     298                lock (Dictionary) 
     299                    return Dictionary.Count; 
     300            } 
     301        } 
     302 
     303        public bool IsReadOnly 
     304        { 
     305            get { return false; } 
     306        } 
     307 
     308        public bool Remove(KeyValuePair<Thread, LogThreadTargets> item) 
     309        { 
     310            lock (Dictionary) 
     311                return Dictionary.Remove(item.Key); 
     312        } 
     313 
     314        #endregion 
     315 
     316        #region IEnumerable<KeyValuePair<Thread,LogThreadTargets>> Members 
     317 
     318        public IEnumerator<KeyValuePair<Thread, LogThreadTargets>> GetEnumerator() 
     319        { 
     320            return Dictionary.GetEnumerator(); 
     321        } 
     322 
     323        #endregion 
     324 
     325        #region IEnumerable Members 
     326 
     327        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
     328        { 
     329            return Dictionary.GetEnumerator(); 
     330        } 
     331 
     332        #endregion 
     333 
     334        /// <summary> 
     335        /// The backing store for this dictionary. 
     336        /// </summary> 
     337        private Dictionary<Thread, LogThreadTargets> Dictionary = 
     338            new Dictionary<Thread, LogThreadTargets>(); 
     339    } 
     340 
     341    public class LogThreadTargets : IList<ILogTarget> 
     342    { 
     343        #region IList<ILogTarget> Members 
     344 
     345        public int IndexOf(ILogTarget item) 
     346        { 
     347            lock (List) 
     348                return List.IndexOf(item); 
     349        } 
     350 
     351        public void Insert(int index, ILogTarget item) 
     352        { 
     353            lock (List) 
     354                List.Insert(index, item); 
     355        } 
     356 
     357        public void RemoveAt(int index) 
     358        { 
     359            lock (List) 
     360                List.RemoveAt(index); 
     361        } 
     362 
     363        public ILogTarget this[int index] 
     364        { 
     365            get 
     366            { 
     367                lock (List) 
     368                    return List[index]; 
     369            } 
     370            set 
     371            { 
     372                lock (List) 
     373                    List[index] = value; 
     374            } 
     375        } 
     376 
     377        #endregion 
     378 
     379        #region ICollection<ILogTarget> Members 
     380 
     381        public void Add(ILogTarget item) 
     382        { 
     383            lock (List) 
     384                List.Add(item); 
     385        } 
     386 
     387        public void Clear() 
     388        { 
     389            lock (List) 
     390                List.Clear(); 
     391        } 
     392 
     393        public bool Contains(ILogTarget item) 
     394        { 
     395            lock (List) 
     396                return List.Contains(item); 
     397        } 
     398 
     399        public void CopyTo(ILogTarget[] array, int arrayIndex) 
     400        { 
     401            lock (List) 
     402                List.CopyTo(array, arrayIndex); 
     403        } 
     404 
     405        public int Count 
     406        { 
     407            get 
     408            { 
     409                lock (List) 
     410                    return List.Count; 
     411            } 
     412        } 
     413 
     414        public bool IsReadOnly 
     415        { 
     416            get { return false; } 
     417        } 
     418 
     419        public bool Remove(ILogTarget item) 
     420        { 
     421            lock (List) 
     422                return List.Remove(item); 
     423        } 
     424 
     425        #endregion 
     426 
     427        #region IEnumerable<ILogTarget> Members 
     428 
     429        public IEnumerator<ILogTarget> GetEnumerator() 
     430        { 
     431            return List.GetEnumerator(); 
     432        } 
     433 
     434        #endregion 
     435 
     436        #region IEnumerable Members 
     437 
     438        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
     439        { 
     440            return List.GetEnumerator(); 
     441        } 
     442 
     443        #endregion 
     444 
     445        /// <summary> 
     446        /// The backing store for this list. 
     447        /// </summary> 
     448        private List<ILogTarget> List = new List<ILogTarget>(); 
     449    } 
     450 
     451    /// <summary> 
     452    /// The logger target interface which all interested listeners of log events must 
     453    /// implement. 
     454    /// </summary> 
     455    public interface ILogTarget 
     456    { 
     457        /// <summary> 
     458        /// The handler for events. 
     459        /// </summary> 
     460        /// <param name="sender">The sender of the event.</param> 
     461        /// <param name="e">The event data associated with the event.</param> 
     462        void OnEventLogged(object sender, LogEventArgs e); 
     463 
     464        /// <summary> 
     465        /// Chains the provided target to the current target, so that when this 
     466        /// target receives an event, the provided target is also executed. 
     467        /// </summary> 
     468        /// <param name="target">The target to chain with the current one.</param> 
     469        /// <remarks>Chaining a target multiple times will cause the target to 
     470        /// be invoked multiple times for every event.</remarks> 
     471        void Chain(ILogTarget target); 
     472 
     473        /// <summary> 
     474        /// Unchains the provided target from the current target, so that the 
     475        /// provided target is no longer invoked when this target receives an event. 
     476        /// </summary> 
     477        /// <param name="target">The target to unchain</param> 
     478        /// <remarks>Multiply-chained targets need to be unchained the same amount 
     479        /// of time to be completely removed.</remarks> 
     480        void Unchain(ILogTarget target); 
     481    } 
     482 
     483    /// <summary> 
     484    /// Registers a provided log target to receive log messages for the lifespan 
     485    /// of this object. 
     486    /// </summary> 
     487    public sealed class LogSession : IDisposable 
     488    { 
     489        /// <summary> 
     490        /// Constructor. Registers the given log target with the provided threads 
     491        /// for listening for log messages. 
     492        /// </summary> 
     493        /// <param name="target">The target that should receive events.</param> 
     494        /// <param name="threads">The threads which the target will be registered 
     495        /// with for event notifications.</param> 
     496        public LogSession(ILogTarget target, params Thread[] threads) 
     497        { 
     498            Target = target; 
     499            Threads = threads.Distinct().ToArray(); 
     500 
     501            foreach (Thread thread in Threads) 
     502                Logger.Listeners[thread].Add(target); 
     503        } 
     504 
     505        /// <summary> 
     506        /// Constructor. Registered the given log target with the current thread 
     507        /// for listening for log messages. 
     508        /// </summary> 
     509        /// <param name="target">The target which should receive events</param> 
     510        public LogSession(ILogTarget target) 
     511            : this(target, Thread.CurrentThread) 
     512        { 
     513        } 
     514 
     515        #region IDisposable Members 
     516 
     517        ~LogSession() 
     518        { 
     519            Dispose(false); 
     520        } 
     521 
     522        private void Dispose(bool disposing) 
     523        { 
     524            if (Threads == null || Target == null) 
     525                return; 
     526 
     527            if (disposing) 
     528            { 
     529                //Disconnect the event handler from the threads. 
     530                foreach (Thread thread in Threads) 
     531                    Logger.Listeners[thread].Remove(Target); 
     532            } 
     533 
     534            Threads = null; 
     535            Target = null; 
     536        } 
     537 
     538        public void Dispose() 
     539        { 
     540            Dispose(true); 
     541            GC.SuppressFinalize(this); 
     542        } 
     543 
     544        #endregion 
     545 
     546        /// <summary> 
     547        /// The target that should receive events. If this is null, the object 
     548        /// has been disposed. 
     549        /// </summary> 
     550        private ILogTarget Target; 
     551 
     552        /// <summary> 
     553        /// The list of threads which the target will be registered with for event 
     554        /// notifications. If this is null, the object is disposd. 
     555        /// </summary> 
     556        private Thread[] Threads; 
     557    } 
     558 
     559    /// <summary> 
     560    /// Collects a list of log entries into one session. 
     561    /// </summary> 
     562    /// <remarks>Instance functions of this class are thread-safe.</remarks> 
     563    public class LogSink : ILogTarget, IList<LogEntry> 
     564    { 
     565        #region ILoggerTarget Members 
     566 
     567        public void OnEventLogged(object sender, LogEventArgs e) 
     568        { 
     569            lock (List) 
     570                List.Add(e.LogEntry); 
     571 
     572            lock (ChainedTargets) 
     573                ChainedTargets.ForEach(target => target.OnEventLogged(sender, e)); 
     574        } 
     575 
     576        public void Chain(ILogTarget target) 
     577        { 
     578            lock (ChainedTargets) 
     579                ChainedTargets.Add(target); 
     580        } 
     581 
     582        public void Unchain(ILogTarget target) 
     583        { 
     584            lock (ChainedTargets) 
     585                ChainedTargets.Remove(target); 
     586        } 
     587 
     588        /// <summary> 
     589        /// The list of targets which are chained to this one. 
     590        /// </summary> 
     591        private List<ILogTarget> ChainedTargets = new List<ILogTarget>(); 
     592 
     593        #endregion 
     594 
     595        #region IList<LogEntry> Members 
     596 
     597        public int IndexOf(LogEntry item) 
     598        { 
     599            lock (List) 
     600                return IndexOf(item); 
     601        } 
     602 
     603        public void Insert(int index, LogEntry item) 
     604        { 
     605            lock (List) 
     606                List.Insert(index, item); 
     607        } 
     608 
     609        public void RemoveAt(int index) 
     610        { 
     611            lock (List) 
     612                List.RemoveAt(index); 
     613        } 
     614 
     615        public LogEntry this[int index] 
     616        { 
     617            get 
     618            { 
     619                lock (List) 
     620                    return List[index]; 
     621            } 
     622            set 
     623            { 
     624                lock (List) 
     625                    List[index] = value; 
     626            } 
     627        } 
     628 
     629        #endregion 
     630 
     631        #region ICollection<LogEntry> Members 
     632 
     633        public void Add(LogEntry item) 
     634        { 
     635            lock (List) 
     636                List.Add(item); 
     637        } 
     638 
     639        public void Clear() 
     640        { 
     641            lock (List) 
     642                List.Clear(); 
     643        } 
     644 
     645        public bool Contains(LogEntry item) 
     646        { 
     647            lock (List) 
     648                return List.Contains(item); 
     649        } 
     650 
     651        public void CopyTo(LogEntry[] array, int arrayIndex) 
     652        { 
     653            lock (List) 
     654                List.CopyTo(array, arrayIndex); 
     655        } 
     656 
     657        public int Count 
     658        { 
     659            get 
     660            { 
     661                lock(List) 
     662                    return Count; 
     663            } 
     664        } 
     665 
     666        public bool IsReadOnly 
     667        { 
     668            get { return true; } 
     669        } 
     670 
     671        public bool Remove(LogEntry item) 
     672        { 
     673            lock (List) 
     674                return List.Remove(item); 
     675        } 
     676 
     677        #endregion 
     678 
     679        #region IEnumerable<LogEntry> Members 
     680 
     681        public IEnumerator<LogEntry> GetEnumerator() 
     682        { 
     683            return List.GetEnumerator(); 
     684        } 
     685 
     686        #endregion 
     687 
     688        #region IEnumerable Members 
     689 
     690        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
     691        { 
     692            return List.GetEnumerator(); 
     693        } 
     694 
     695        #endregion 
     696 
     697        /// <summary> 
     698        /// Gets the highest log level in the current log sink. 
     699        /// </summary> 
     700        public LogLevel Highest 
     701        { 
     702            get 
     703            { 
     704                lock (List) 
     705                    return List.Max(delegate(LogEntry e) { return e.Level; }); 
     706            } 
     707        } 
     708 
     709        /// <summary> 
     710        /// Gets the time the first message was logged. 
     711        /// </summary> 
     712        public DateTime StartTime 
     713        { 
     714            get 
     715            { 
     716                lock (List) 
     717                    return List.First().Timestamp; 
     718            } 
     719        } 
     720 
     721        /// <summary> 
     722        /// Gets the time the last message was logged. 
     723        /// </summary> 
     724        public DateTime EndTime 
     725        { 
     726            get 
     727            { 
     728                lock (List) 
     729                    return List.Last().Timestamp; 
     730            } 
     731        } 
     732 
     733        /// <summary> 
     734        /// The backing store of this session. 
     735        /// </summary> 
     736        private List<LogEntry> List = new List<LogEntry>(); 
     737    } 
    532738} 
  • branches/eraser6/CodeReview/Eraser.Util/Theming.cs

    r1686 r1777  
    188188        /// </summary> 
    189189        private static Dictionary<ToolStrip, UXThemeMenuRenderer> ThemedMenus = 
    190             new Dictionary<ToolStrip,UXThemeMenuRenderer>(); 
     190            new Dictionary<ToolStrip, UXThemeMenuRenderer>(); 
    191191 
    192192        /// <summary> 
  • branches/eraser6/CodeReview/Eraser/LogForm.cs

    r1768 r1777  
    2323using System.Collections.Generic; 
    2424using System.ComponentModel; 
    25 using System.Data; 
     25using System.Linq; 
    2626using System.Drawing; 
    2727using System.Text; 
     
    3535namespace Eraser 
    3636{ 
    37     public partial class LogForm : Form 
     37    public partial class LogForm : Form, ILogTarget 
    3838    { 
    3939        public LogForm(Task task) 
     
    4646 
    4747            //Populate the list of sessions 
    48             foreach (DateTime session in task.Log.Entries.Keys) 
    49                 filterSessionCombobox.Items.Add(session); 
    50             if (task.Log.Entries.Keys.Count != 0) 
     48            foreach (LogSink sink in task.Log) 
     49                filterSessionCombobox.Items.Add(sink.StartTime); 
     50            if (filterSessionCombobox.Items.Count != 0) 
    5151                filterSessionCombobox.SelectedIndex = filterSessionCombobox.Items.Count - 1; 
    5252 
     
    6161 
    6262            //Register our event handler to get live log messages 
    63             Task.Log.Logged += task_Logged; 
    64             Task.Log.NewSession += task_NewSession; 
     63            if (Task.Log.Count > 0) 
     64                Task.Log.Last().Chain(this); 
     65            Task.TaskStarted += task_TaskStarted; 
    6566        } 
    6667 
    6768        private void LogForm_FormClosed(object sender, FormClosedEventArgs e) 
    6869        { 
    69             Task.Log.NewSession -= task_NewSession; 
    70             Task.Log.Logged -= task_Logged; 
     70            Task.TaskStarted -= task_TaskStarted; 
     71            if (Task.Log.Count > 0) 
     72                Task.Log.Last().Unchain(this); 
    7173        } 
    7274 
     
    7678        } 
    7779 
    78         private void task_NewSession(object sender, EventArgs e) 
     80        private void task_TaskStarted(object sender, EventArgs e) 
    7981        { 
    8082            if (IsDisposed || !IsHandleCreated) 
     
    8284            if (InvokeRequired) 
    8385            { 
    84                 Invoke((EventHandler<EventArgs>)task_NewSession, sender, e); 
    85                 return; 
    86             } 
    87  
    88             filterSessionCombobox.Items.Add(Task.Log.LastSession); 
    89         } 
    90  
    91         private void task_Logged(object sender, LogEventArgs e) 
    92         { 
    93             if (IsDisposed || !IsHandleCreated) 
    94                 return; 
    95             if (InvokeRequired) 
    96             { 
    97                 Invoke((EventHandler<LogEventArgs>)task_Logged, sender, e); 
    98                 return; 
    99             } 
    100  
    101             //Check whether the current entry meets the criteria for display. Since 
    102             //this is an event handler for new log messages only, we should only 
    103             //display this entry when the session in question is the last one. 
    104             if (filterSessionCombobox.SelectedItem == null || 
    105                 (DateTime)filterSessionCombobox.SelectedItem != Task.Log.LastSession || 
    106                 !MeetsCriteria(e.LogEntry)) 
    107             { 
    108                 return; 
    109             } 
    110  
    111             //Add it to the cache and increase our virtual list size. 
    112             EntryCache.Add(e.LogEntry); 
    113             ++log.VirtualListSize; 
    114  
    115             //Enable the clear and copy log buttons only if we have entries to copy. 
    116             EnableButtons(); 
     86                Invoke((EventHandler<EventArgs>)task_TaskStarted, sender, e); 
     87                return; 
     88            } 
     89 
     90            filterSessionCombobox.Items.Add(Task.Log.Last().StartTime); 
    11791        } 
    11892 
     
    265239        } 
    266240 
     241        #region ILogTarget Members 
     242 
     243        public void OnEventLogged(object sender, LogEventArgs e) 
     244        { 
     245            if (IsDisposed || !IsHandleCreated) 
     246                return; 
     247            if (InvokeRequired) 
     248            { 
     249                Invoke((EventHandler<LogEventArgs>)OnEventLogged, sender, e); 
     250                return; 
     251            } 
     252 
     253            //Check whether the current entry meets the criteria for display. 
     254            if (filterSessionCombobox.SelectedItem == null || !MeetsCriteria(e.LogEntry)) 
     255            { 
     256                return; 
     257            } 
     258 
     259            //Add it to the cache and increase our virtual list size. 
     260            EntryCache.Add(e.LogEntry); 
     261            ++log.VirtualListSize; 
     262 
     263            //Enable the clear and copy log buttons only if we have entries to copy. 
     264            EnableButtons(); 
     265        } 
     266 
     267        public void Chain(ILogTarget target) 
     268        { 
     269            throw new NotImplementedException(); 
     270        } 
     271 
     272        public void Unchain(ILogTarget target) 
     273        { 
     274            throw new NotImplementedException(); 
     275        } 
     276 
     277        #endregion 
     278 
    267279        /// <summary> 
    268280        /// Checks whether the given log entry meets the current display criteria. 
     
    305317 
    306318            Application.UseWaitCursor = true; 
    307             LogSessionDictionary log = Task.Log.Entries; 
     319            LogSink sink = Task.Log[filterSessionCombobox.SelectedIndex]; 
    308320            EntryCache.Clear(); 
    309321            SelectedEntries.Clear(); 
    310  
    311             //Iterate over every key 
    312             foreach (DateTime sessionTime in log.Keys) 
    313             { 
    314                 //Check for the session time 
    315                 if (filterSessionCombobox.SelectedItem == null ||  
    316                     sessionTime != (DateTime)filterSessionCombobox.SelectedItem) 
    317                     continue; 
    318  
    319                 foreach (LogEntry entry in log[sessionTime]) 
    320                 { 
    321                     //Check if the entry meets the criteria for viewing 
    322                     if (MeetsCriteria(entry)) 
    323                         EntryCache.Add(entry); 
    324                 } 
    325             } 
     322            EntryCache.AddRange(sink.Where(MeetsCriteria)); 
    326323 
    327324            //Set the list view size and update all the control states 
    328             this.log.VirtualListSize = EntryCache.Count; 
    329             this.log.Refresh(); 
     325            log.VirtualListSize = EntryCache.Count; 
     326            log.Refresh(); 
    330327            EnableButtons(); 
    331328            Application.UseWaitCursor = false; 
     
    337334        private void EnableButtons() 
    338335        { 
    339             clear.Enabled = Task.Log.Entries.Count > 0; 
     336            clear.Enabled = Task.Log.Count > 0; 
    340337        } 
    341338 
  • branches/eraser6/CodeReview/Eraser/ProgressForm.cs

    r1765 r1777  
    2222using System; 
    2323using System.Collections.Generic; 
    24 using System.Data; 
     24using System.Linq; 
    2525using System.Drawing; 
    2626using System.Text; 
     
    109109 
    110110            //Inform the user on the status of the task. 
    111             LogLevel highestLevel = LogLevel.Information; 
    112             LogEntryCollection entries = task.Log.LastSessionEntries; 
    113             foreach (LogEntry log in entries) 
    114                 if (log.Level > highestLevel) 
    115                     highestLevel = log.Level; 
    116  
     111            LogLevel highestLevel = task.Log.Last().Highest; 
    117112            switch (highestLevel) 
    118113            { 
  • branches/eraser6/CodeReview/Eraser/SchedulerPanel.cs

    r1768 r1777  
    2222using System; 
    2323using System.Collections.Generic; 
    24 using System.Data; 
     24using System.Linq; 
    2525using System.Drawing; 
    2626using System.Text; 
     
    252252 
    253253            //Get the exit status of the task. 
    254             LogLevel highestLevel = LogLevel.Information; 
    255             LogEntryCollection logs = task.Log.LastSessionEntries; 
    256             foreach (LogEntry log in logs) 
    257                 if (log.Level > highestLevel) 
    258                     highestLevel = log.Level; 
     254            LogLevel highestLevel = task.Log.Last().Highest; 
    259255 
    260256            //Show a balloon to inform the user 
    261257            MainForm parent = (MainForm)FindForm(); 
    262  
    263             //TODO: Is this still needed? 
    264             if (parent == null) 
    265                 throw new InvalidOperationException(); 
    266258            if (parent.WindowState == FormWindowState.Minimized || !parent.Visible) 
    267259            { 
Note: See TracChangeset for help on using the changeset viewer.