/// <summary> /// Initialises a new instance of the FieldLogExceptionItem class. /// </summary> /// <param name="priority">The priority of the new log item.</param> /// <param name="type">The scope type.</param> /// <param name="name">The scope name.</param> /// <param name="webRequestData">The web request data. This parameter is required for the WebRequestStart scope type.</param> public FieldLogScopeItem(FieldLogPriority priority, FieldLogScopeType type, string name, FieldLogWebRequestData webRequestData) : base(priority) { Type = type; Level = FL.ScopeLevel; Name = name; if (Type == FieldLogScopeType.ThreadStart) { IsBackgroundThread = Thread.CurrentThread.IsBackground; IsPoolThread = Thread.CurrentThread.IsThreadPoolThread; Thread = Thread.CurrentThread; } if (Type == FieldLogScopeType.LogStart) { EnvironmentData = FieldLogEventEnvironment.Current(); Size += EnvironmentData.Size; } if (Type == FieldLogScopeType.WebRequestStart) { if (webRequestData == null) { throw new ArgumentNullException("webRequestData", "The webRequestData parameter is required for the WebRequestStart scope type."); } WebRequestData = webRequestData; Size += WebRequestData.Size; } Size += 4 + 4 + (Name != null ? Name.Length * 2 : 0) + 4 + 4 + 4 + 4 + 4; }
/// <summary> /// Initialises a new instance of the FieldLogExceptionItem class. /// </summary> /// <param name="priority">The priority of the new log item.</param> /// <param name="ex">The exception instance.</param> /// <param name="context">The context in which the exception has been thrown. Can be an /// arbitrary string that is useful for the logging purpose.</param> /// <param name="customStackTrace">A StackTrace that shall be logged instead of the StackTrace from the Exception instance.</param> public FieldLogExceptionItem(FieldLogPriority priority, Exception ex, string context, StackTrace customStackTrace) : base(priority) { Exception = new FieldLogException(ex, customStackTrace); Context = context; bool includeEnvironment = true; if (context == "AppDomain.FirstChanceException" || context == FL.StackTraceOnlyExceptionContext) { // First-chance exception logging with environment may lead to crashes at WMI // requests, so it's disabled for now. // Testcase: Inspect FieldLogViewer with Snoop while debugging. includeEnvironment = false; } if (includeEnvironment) { EnvironmentData = FieldLogEventEnvironment.Current(); } else { EnvironmentData = FieldLogEventEnvironment.Empty; } Size += Exception.Size + (Context != null ? Context.Length * 2 : 0) + EnvironmentData.Size; }
/// <summary> /// Initialises a new instance of the FieldLogDataItem class. /// </summary> /// <param name="priority">The priority of the new log item.</param> /// <param name="name">The name of the data item. Can be an arbitrary string that is useful for the logging purpose.</param> /// <param name="value">The value of the data item. Will be converted to a string. Line breaks are allowed for structuring.</param> public FieldLogDataItem(FieldLogPriority priority, string name, object value) : base(priority) { Name = name; Value = FormatValues(value); Size += (Name != null ? Name.Length * 2 : 0) + (Value != null ? Value.Length * 2 : 0); }
/// <summary> /// Initialises a new instance of the FieldLogTextItem class. /// </summary> /// <param name="priority">The priority of the new log item.</param> /// <param name="text">The text message.</param> /// <param name="details">Additional details of the log event.</param> public FieldLogTextItem(FieldLogPriority priority, string text, string details) : base(priority) { Text = text; Details = details; Size += (Text != null ? Text.Length * 2 : 0) + (Details != null ? Details.Length * 2 : 0); }
/// <summary> /// Appends more text to the details of the log item. /// </summary> /// <param name="priority">The new priority of the log item. This will only be changed if the priority is increased.</param> /// <param name="text">The text to append.</param> public void AppendDetails(FieldLogPriority priority, string text) { if (priority > Priority) { Priority = priority; } Details += (Details != null && !Details.EndsWith("\n") ? Environment.NewLine : "") + text; Size += text.Length * 2; }
/// <summary> /// Initialises a new instance of the FieldLogFileGroupReader class. This sets up log /// readers for each priority and links additional readers for existing files to them. /// A FileSystemWatcher is set up to add new files as they are created. /// </summary> /// <param name="basePath">The path and file prefix of the log files to read.</param> /// <param name="singleFile">true to load a single file only. <paramref name="basePath"/> /// must be a full file name then.</param> /// <param name="readWaitHandle">The wait handle that will be signalled after all files /// have been read to the end and if the last reader is now going to wait for further data /// to be appended to the file.</param> public FieldLogFileGroupReader(string basePath, bool singleFile = false, EventWaitHandle readWaitHandle = null) { var prioValues = Enum.GetValues(typeof(FieldLogPriority)); readTasks = new Task <bool> [prioValues.Length]; if (!singleFile) { // Start file system watcher to detect new files string logDir = Path.GetDirectoryName(basePath); string logFile = Path.GetFileName(basePath); fsw = new FileSystemWatcher(logDir, logFile + "-*.fl"); fsw.NotifyFilter = NotifyFilters.FileName; fsw.Created += fsw_Created; fsw.EnableRaisingEvents = true; // Find all log files for every priority foreach (FieldLogPriority prio in prioValues) { FindLogFiles(basePath, prio); } } else { Match m = Regex.Match(basePath, @"-([0-9])-[0-9]{18}\.fl"); if (m.Success) { FieldLogPriority prio = (FieldLogPriority)int.Parse(m.Groups[1].Value); AddNewReader(prio, basePath, false); } else { throw new ArgumentException("The file name cannot be analysed."); } } // Wait for all priorities to be read to the end, then signal one event if (readWaitHandle != null) { Task.Factory.StartNew(() => ReadWaitHandleTask(readWaitHandle)); } waitForNewFilePrioTask = Task.Factory.StartNew <bool>(WaitForNewFilePrio); closeTask = Task.Factory.StartNew <bool>(WaitForClose); }
/// <summary> /// Finds all currently existing log files and adds a new reader for each of them. /// </summary> /// <param name="basePath">The directory and file base name.</param> /// <param name="prio">The priority of files to find.</param> private void FindLogFiles(string basePath, FieldLogPriority prio) { lock (readerLock) { // Remove any files that were added from the FileSystemWatcher event before we // started scanning for files. Those files will now be found again anyway. readers[prio] = null; string logDir = Path.GetDirectoryName(basePath); string logFile = Path.GetFileName(basePath); List <string> fileNames = new List <string>(Directory.GetFiles(logDir, logFile + "-" + (int)prio + "-*.fl")); fileNames.Sort(); foreach (string fileName in fileNames) { AddNewReader(prio, fileName, false); } } }
/// <summary> /// Creates a new log file reader and adds it to the priority's log file enumerator. /// </summary> /// <param name="prio">The priority of files to write.</param> /// <param name="fileName">The name of the log file.</param> /// <param name="fromFsw">Indicates whether the reader was created from a FileSystemWatcher event.</param> private void AddNewReader(FieldLogPriority prio, string fileName, bool fromFsw) { // Must be within a lock(readerLock)! FL.Trace("AddNewReader, prio=" + prio + ", fileName=" + Path.GetFileName(fileName) + ", fromFsw=" + fromFsw); // Reject the new file if it's already in the queue (delayed FSW event after active scan) if (readers.ContainsKey(prio) && readers[prio] != null && readers[prio].ContainsFile(fileName)) { // This file is already current or queued FL.Checkpoint("This file is already current or queued"); return; } var reader = new FieldLogFileReader(fileName, true); ManualResetEvent h; if (!prioReadSignals.TryGetValue(prio, out h)) { h = new ManualResetEvent(false); prioReadSignals[prio] = h; } reader.ReadWaitHandle = h; if (!readers.ContainsKey(prio) || readers[prio] == null) { // This is the first file of this priority readers[prio] = new FieldLogFileEnumerator(reader); readers[prio].Error += FieldLogFileEnumerator_Error; readTasks[(int)prio] = Task <bool> .Factory.StartNew(readers[prio].MoveNext); // Signal the blocking ReadLogItem method that there's a new reader now newFilePrioEvent.Set(); } else { // Chain the new reader after the last reader in the queue readers[prio].Append(reader, fromFsw); // TODO,DEBUG: What for? //newFilePrioEvent.Set(); } }
/// <summary> /// Called when the FileSystemWatcher found a newly created file of the currently used /// log file set. /// </summary> /// <param name="sender">Unused.</param> /// <param name="args">Event arguments.</param> private void fsw_Created(object sender, FileSystemEventArgs args) { if (closeEvent.WaitOne(0)) { return; // Already closing... } // Ensure it's a file, not a directory if (File.Exists(args.FullPath)) { lock (readerLock) { Match m = Regex.Match(args.FullPath, @"-([0-9])-[0-9]{18}\.fl"); if (m.Success) { FieldLogPriority prio = (FieldLogPriority)int.Parse(m.Groups[1].Value); AddNewReader(prio, args.FullPath, true); } } } }
/// <summary> /// Initialises a new instance of the FieldLogItem class. /// </summary> /// <param name="priority">The priority of the new log item.</param> protected FieldLogItem(FieldLogPriority priority) { Time = FL.UtcNow; Priority = priority; SessionId = FL.SessionId; if (FL.ThreadId == 0) { FL.ThreadId = Thread.CurrentThread.ManagedThreadId; } ThreadId = FL.ThreadId; #if ASPNET if (HttpContext.Current != null) { object value = HttpContext.Current.Items[FL.HttpContextKey_WebRequestId]; if (value is uint) { WebRequestId = (uint)value; } } #endif Size = 4 + 4 + 8 + 4 + 16 + 4 + 4 + 4 + 4; }
/// <summary> /// Initialises a new instance of the FieldLogExceptionItem class. /// </summary> /// <param name="priority">The priority of the new log item.</param> /// <param name="type">The scope type.</param> /// <param name="name">The scope name.</param> public FieldLogScopeItem(FieldLogPriority priority, FieldLogScopeType type, string name) : this(priority, type, name, null) { }
/// <summary> /// Initialises a new instance of the FieldLogExceptionItem class. /// </summary> /// <param name="priority">The priority of the new log item.</param> /// <param name="ex">The exception instance.</param> /// <param name="context">The context in which the exception has been thrown. Can be an /// arbitrary string that is useful for the logging purpose.</param> public FieldLogExceptionItem(FieldLogPriority priority, Exception ex, string context) : this(priority, ex, context, null) { }
/// <summary> /// Initialises a new instance of the FieldLogExceptionItem class. /// </summary> /// <param name="priority">The priority of the new log item.</param> /// <param name="ex">The exception instance.</param> public FieldLogExceptionItem(FieldLogPriority priority, Exception ex) : this(priority, ex, null, null) { }
/// <summary> /// Initialises a new instance of the <see cref="FieldLogFileWriter"/> class. /// </summary> /// <param name="fileName">The name of the log file to write to.</param> /// <param name="prio">The priority of the items in the log file.</param> public FieldLogFileWriter(string fileName, FieldLogPriority prio) { FileName = fileName; fileStream = File.Open(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read); //Match m = Regex.Match(fileName, "[^0-9]([0-9]{8}-[0-9]{6})[^0-9]"); //if (m.Success) //{ // createdTime = DateTime.ParseExact(m.Groups[1].Value, "yyyyMMdd-HHmmss", CultureInfo.InvariantCulture); //} FileInfo fi = new FileInfo(fileName); createdTime = fi.CreationTimeUtc; try { // Setting the NTFS compression needs the file to be opened with FileAccess.ReadWrite int lpBytesReturned = 0; short COMPRESSION_FORMAT_DEFAULT = 1; int result = DeviceIoControl(fileStream.SafeFileHandle.DangerousGetHandle(), FSCTL_SET_COMPRESSION, ref COMPRESSION_FORMAT_DEFAULT, 2 /*sizeof(short)*/, IntPtr.Zero, 0, ref lpBytesReturned, IntPtr.Zero); int err = Marshal.GetLastWin32Error(); } catch { // NTFS or Win32 may not be available } byte[] fileHeaderBytes = Encoding.UTF8.GetBytes(fileHeader); if (fileStream.Length == 0) { // Initialise file with header fileStream.Write(fileHeaderBytes, 0, fileHeaderBytes.Length); fileStream.WriteByte(FL.FileFormatVersion); // File format version // Rewrite currently open scope items with RepeatedScope item type if (FL.LogScopeItem != null) { if (FL.LogScopeItem.Priority == prio && FL.LogScopeItem.WasWritten) { FL.LogScopeItem.IsRepeated = true; FL.LogScopeItem.Write(this); } } foreach (FieldLogScopeItem item in new List<FieldLogScopeItem>(FL.ThreadScopes.Values)) { if (item.Priority == prio && item.WasWritten) { if (!item.Thread.IsAlive) { // Don't repeat dead threads FL.ThreadScopes.Remove(item.ThreadId); continue; } item.IsRepeated = true; item.Write(this); } } foreach (FieldLogScopeItem item in new List<FieldLogScopeItem>(FL.WebRequestScopes.Values)) { if (item.Priority == prio && item.WasWritten) { item.IsRepeated = true; item.Write(this); } } foreach (var stack in FL.CurrentScopes.Values) { foreach (FieldLogScopeItem item in stack.ToArray()) { if (item.Priority == prio && item.WasWritten) { item.IsRepeated = true; item.Write(this); } } } } else { // Validate existing file byte[] bytes = new byte[fileHeaderBytes.Length]; if (fileStream.Read(bytes, 0, fileHeaderBytes.Length) < fileHeaderBytes.Length) { throw new FormatException("Invalid log file to append to. Header too short."); } for (int i = 0; i < fileHeaderBytes.Length; i++) if (bytes[i] != fileHeaderBytes[i]) throw new FormatException("Invalid log file to append to. Wrong header."); int formatVersion = fileStream.ReadByte(); if (formatVersion != FL.FileFormatVersion) { throw new FormatException("Invalid log file to append to. Unsupported file format version (" + formatVersion + ")."); } // Read existing texts into the cache to re-use them ReadTextCache(); } // Append any new log items to the end of the file (just to be sure) fileStream.Seek(0, SeekOrigin.End); }
/// <summary> /// Initialises a new instance of the FieldLogTextItem class. /// </summary> /// <param name="priority">The priority of the new log item.</param> /// <param name="text">The text message.</param> public FieldLogTextItem(FieldLogPriority priority, string text) : this(priority, text, null) { }
/// <summary> /// Creates a new log file reader and adds it to the priority's log file enumerator. /// </summary> /// <param name="prio">The priority of files to write.</param> /// <param name="fileName">The name of the log file.</param> /// <param name="fromFsw">Indicates whether the reader was created from a FileSystemWatcher event.</param> private void AddNewReader(FieldLogPriority prio, string fileName, bool fromFsw) { // Must be within a lock(readerLock)! FL.Trace("AddNewReader, prio=" + prio + ", fileName=" + Path.GetFileName(fileName) + ", fromFsw=" + fromFsw); // Reject the new file if it's already in the queue (delayed FSW event after active scan) if (readers.ContainsKey(prio) && readers[prio] != null && readers[prio].ContainsFile(fileName)) { // This file is already current or queued FL.Checkpoint("This file is already current or queued"); return; } var reader = new FieldLogFileReader(fileName, true); ManualResetEvent h; if (!prioReadSignals.TryGetValue(prio, out h)) { h = new ManualResetEvent(false); prioReadSignals[prio] = h; } reader.ReadWaitHandle = h; if (!readers.ContainsKey(prio) || readers[prio] == null) { // This is the first file of this priority readers[prio] = new FieldLogFileEnumerator(reader); readers[prio].Error += FieldLogFileEnumerator_Error; readTasks[(int)prio] = Task<bool>.Factory.StartNew(readers[prio].MoveNext); // Signal the blocking ReadLogItem method that there's a new reader now newFilePrioEvent.Set(); } else { // Chain the new reader after the last reader in the queue readers[prio].Append(reader, fromFsw); // TODO,DEBUG: What for? //newFilePrioEvent.Set(); } }
/// <summary> /// Finds all currently existing log files and adds a new reader for each of them. /// </summary> /// <param name="basePath">The directory and file base name.</param> /// <param name="prio">The priority of files to find.</param> private void FindLogFiles(string basePath, FieldLogPriority prio) { lock (readerLock) { // Remove any files that were added from the FileSystemWatcher event before we // started scanning for files. Those files will now be found again anyway. readers[prio] = null; string logDir = Path.GetDirectoryName(basePath); string logFile = Path.GetFileName(basePath); List<string> fileNames = new List<string>(Directory.GetFiles(logDir, logFile + "-" + (int)prio + "-*.fl")); fileNames.Sort(); foreach (string fileName in fileNames) { AddNewReader(prio, fileName, false); } } }
private bool ComparePriority(FieldLogPriority prio) { FieldLogPriority filterPrio; if (Enum.TryParse(Value, out filterPrio)) { switch (Comparison) { case FilterComparison.Equals: case FilterComparison.NotEquals: return prio == filterPrio; case FilterComparison.GreaterOrEqual: return prio >= filterPrio; case FilterComparison.GreaterThan: return prio > filterPrio; case FilterComparison.LessOrEqual: return prio <= filterPrio; case FilterComparison.LessThan: return prio < filterPrio; default: throw new Exception("Invalid comparison for Priority column: " + Comparison); } } else { // Invalid value for Priority column return false; } }
/// <summary> /// Reads the next log item from the log file group. If all files have been read until the /// end, this method blocks until a new log item was written to any file, or until the /// close event was set. /// </summary> /// <returns>The next log item, or null if there are no more log items and the waiting was cancelled.</returns> public FieldLogItem ReadLogItem() { while (true) { Task <bool>[] availableTasks; lock (readerLock) { availableTasks = readTasks .Where(t => t != null) .Concat(new Task <bool>[] { waitForNewFilePrioTask }) .Concat(new Task <bool>[] { closeTask }) .ToArray(); } Task.WaitAny(availableTasks); // We don't care about which task has finished. It may actually be multiple tasks. // We just test them all and use the result of all tasks that have finished. // Compare all completed readers' current value, find the smallest time and move // that enumerator one further. DateTime minTime = DateTime.MaxValue; FieldLogItem minTimeItem = null; FieldLogPriority minTimePrio = 0; foreach (var availableTask in availableTasks) { if (availableTask.IsCompleted) { // Search from the lowest priority up, as lower priorities appear more often for (int prioInt = 0; prioInt < readTasks.Length; prioInt++) { if (availableTask == readTasks[prioInt]) { // A reader enumerator task has finished, consider its result FieldLogPriority prio = (FieldLogPriority)prioInt; if (availableTask.Result) { // A new item of this priority is available. // Fetch the item from the reader. FieldLogItem item = readers[prio].Current; if (item.Time < minTime) { // The item's time is before any other item in this run, remember it minTime = item.Time; minTimeItem = item; minTimePrio = prio; } } else { // This priority's reader has finished, remove it. // Lock so that AddNewReader won't mess up if it finds a new file for this priority. lock (readerLock) { readers[prio].Dispose(); readers[prio] = null; readTasks[prioInt] = null; } } } } if (availableTask == waitForNewFilePrioTask) { // A file of a new priority was added and the WaitAny was interrupted, // to restart with the newly added reader added to the list of // available tasks. // Recreate the signal task and continue. waitForNewFilePrioTask = Task <bool> .Factory.StartNew(WaitForNewFilePrio); } if (availableTask == closeTask) { // The reader was requested to close. // Close all current enumerators, which then close all readers and // everything should tidy up itself... if (fsw != null) { fsw.EnableRaisingEvents = false; fsw.Dispose(); } foreach (var reader in readers.Values) { if (reader != null) { reader.Close(); } } return(null); } } } if (minTimeItem != null) { // We found an item. // Create new task for the next item of this priority var task = Task <bool> .Factory.StartNew(readers[minTimePrio].MoveNext); readTasks[(int)minTimePrio] = task; // Now return the next log item in time of all that are currently available return(minTimeItem); } // Restart this loop and wait for the next event } }
/// <summary> /// Initialises a new instance of the FieldLogExceptionItem class. /// </summary> /// <param name="priority">The priority of the new log item.</param> /// <param name="type">The scope type.</param> /// <param name="name">The scope name.</param> /// <param name="webRequestData">The web request data. This parameter is required for the WebRequestStart scope type.</param> public FieldLogScopeItem(FieldLogPriority priority, FieldLogScopeType type, string name, FieldLogWebRequestData webRequestData) : base(priority) { Type = type; Level = FL.ScopeLevel; Name = name; if (Type == FieldLogScopeType.ThreadStart) { IsBackgroundThread = Thread.CurrentThread.IsBackground; IsPoolThread = Thread.CurrentThread.IsThreadPoolThread; Thread = Thread.CurrentThread; } if (Type == FieldLogScopeType.LogStart) { EnvironmentData = FieldLogEventEnvironment.Current(); Size += EnvironmentData.Size; } if (Type == FieldLogScopeType.WebRequestStart) { if (webRequestData == null) throw new ArgumentNullException("webRequestData", "The webRequestData parameter is required for the WebRequestStart scope type."); WebRequestData = webRequestData; Size += WebRequestData.Size; } Size += 4 + 4 + (Name != null ? Name.Length * 2 : 0) + 4 + 4 + 4 + 4 + 4; }
/// <summary> /// Initialises a new instance of the <see cref="FieldLogFileWriter"/> class. /// </summary> /// <param name="fileName">The name of the log file to write to.</param> /// <param name="prio">The priority of the items in the log file.</param> public FieldLogFileWriter(string fileName, FieldLogPriority prio) { FileName = fileName; fileStream = File.Open(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read); //Match m = Regex.Match(fileName, "[^0-9]([0-9]{8}-[0-9]{6})[^0-9]"); //if (m.Success) //{ // createdTime = DateTime.ParseExact(m.Groups[1].Value, "yyyyMMdd-HHmmss", CultureInfo.InvariantCulture); //} FileInfo fi = new FileInfo(fileName); createdTime = fi.CreationTimeUtc; try { // Setting the NTFS compression needs the file to be opened with FileAccess.ReadWrite int lpBytesReturned = 0; short COMPRESSION_FORMAT_DEFAULT = 1; int result = DeviceIoControl(fileStream.SafeFileHandle.DangerousGetHandle(), FSCTL_SET_COMPRESSION, ref COMPRESSION_FORMAT_DEFAULT, 2 /*sizeof(short)*/, IntPtr.Zero, 0, ref lpBytesReturned, IntPtr.Zero); int err = Marshal.GetLastWin32Error(); } catch { // NTFS or Win32 may not be available } byte[] fileHeaderBytes = Encoding.UTF8.GetBytes(fileHeader); if (fileStream.Length == 0) { // Initialise file with header fileStream.Write(fileHeaderBytes, 0, fileHeaderBytes.Length); fileStream.WriteByte(FL.FileFormatVersion); // File format version // Rewrite currently open scope items with RepeatedScope item type if (FL.LogScopeItem != null) { if (FL.LogScopeItem.Priority == prio && FL.LogScopeItem.WasWritten) { FL.LogScopeItem.IsRepeated = true; FL.LogScopeItem.Write(this); } } foreach (FieldLogScopeItem item in new List <FieldLogScopeItem>(FL.ThreadScopes.Values)) { if (item.Priority == prio && item.WasWritten) { if (!item.Thread.IsAlive) { // Don't repeat dead threads FL.ThreadScopes.Remove(item.ThreadId); continue; } item.IsRepeated = true; item.Write(this); } } foreach (FieldLogScopeItem item in new List <FieldLogScopeItem>(FL.WebRequestScopes.Values)) { if (item.Priority == prio && item.WasWritten) { item.IsRepeated = true; item.Write(this); } } foreach (var stack in FL.CurrentScopes.Values) { foreach (FieldLogScopeItem item in stack.ToArray()) { if (item.Priority == prio && item.WasWritten) { item.IsRepeated = true; item.Write(this); } } } } else { // Validate existing file byte[] bytes = new byte[fileHeaderBytes.Length]; if (fileStream.Read(bytes, 0, fileHeaderBytes.Length) < fileHeaderBytes.Length) { throw new FormatException("Invalid log file to append to. Header too short."); } for (int i = 0; i < fileHeaderBytes.Length; i++) { if (bytes[i] != fileHeaderBytes[i]) { throw new FormatException("Invalid log file to append to. Wrong header."); } } int formatVersion = fileStream.ReadByte(); if (formatVersion != FL.FileFormatVersion) { throw new FormatException("Invalid log file to append to. Unsupported file format version (" + formatVersion + ")."); } // Read existing texts into the cache to re-use them ReadTextCache(); } // Append any new log items to the end of the file (just to be sure) fileStream.Seek(0, SeekOrigin.End); }
/// <summary> /// Logs an exception raised in the task. Call this like the ContinueWith method with the /// <see cref="TaskContinuationOptions.OnlyOnFaulted"/> option. /// </summary> /// <typeparam name="TResult">The type of the result produced by this Task.</typeparam> /// <param name="task"></param> /// <param name="prio">The priority of the log item.</param> /// <param name="taskName">The name of the task, used for the exception log item context.</param> /// <returns>A new continuation <see cref="Task"/>.</returns> public static Task LogFaulted <TResult>(this Task <TResult> task, FieldLogPriority prio, string taskName) { return(task.ContinueWith(t => FL.Exception(prio, t.Exception, taskName + " Task"), TaskContinuationOptions.OnlyOnFaulted)); }
private void WriteToFieldLog(string source, TraceEventType eventType, int id, string msg) { string shortMsg = null; // Name comparisons roughly in a descending order of frequency, to optimise performance if (source == PresentationTraceSources.DataBindingSource.Name) { HandleDataBindingMessage(id, ref msg, ref shortMsg); } else if (source == PresentationTraceSources.RoutedEventSource.Name) { if (id == 3) { if (eventType == TraceEventType.Start) { eventType = TraceEventType.Verbose; // Don't indent for this } else if (eventType == TraceEventType.Stop) { return; // Don't log this } } //if (id == 4) //{ // return; // Don't log this //} HandleRoutedEventMessage(id, ref msg, ref shortMsg); } else if (source == PresentationTraceSources.ResourceDictionarySource.Name) { HandleResourceDictionaryMessage(id, ref msg, ref shortMsg); } else if (source == PresentationTraceSources.MarkupSource.Name) { HandleMarkupMessage(id, ref msg, ref shortMsg); } else if (source == PresentationTraceSources.AnimationSource.Name) { HandleAnimationMessage(id, ref msg, ref shortMsg); } else if (source == PresentationTraceSources.DependencyPropertySource.Name) { HandleDependencyPropertyMessage(id, ref msg, ref shortMsg); } else if (source == PresentationTraceSources.FreezableSource.Name) { if (id == 1) { return; // Don't log this, it appears everywhere and comes from bugs in WPF } HandleFreezableMessage(id, ref msg, ref shortMsg); } else if (source == PresentationTraceSources.HwndHostSource.Name) { HandleHwndHostMessage(id, ref msg, ref shortMsg); } else if (source == PresentationTraceSources.NameScopeSource.Name) { HandleNameScopeMessage(id, ref msg, ref shortMsg); } else if (source == PresentationTraceSources.ShellSource.Name) { HandleShellMessage(id, ref msg, ref shortMsg); } // DocumentsSource does not have any information and is not augmented here // Fallback short message if unknown event ID if (shortMsg == null) { shortMsg = "ID " + id; } // Select appropriate FieldLog priority or otherwise highlight event type FieldLogPriority prio = FieldLogPriority.Trace; switch (eventType) { case TraceEventType.Critical: case TraceEventType.Error: prio = FieldLogPriority.Error; break; case TraceEventType.Warning: prio = FieldLogPriority.Warning; break; case TraceEventType.Information: prio = FieldLogPriority.Info; break; case TraceEventType.Start: //shortMsg += " [Start]"; break; case TraceEventType.Stop: shortMsg += " [Stop]"; break; case TraceEventType.Suspend: shortMsg += " [Suspend]"; break; case TraceEventType.Resume: shortMsg += " [Resume]"; break; case TraceEventType.Transfer: shortMsg += " [Transfer]"; break; } // Write message to the log if it's still there if (!FL.IsShutdown) { if (eventType == TraceEventType.Stop) { indentLevel--; } string indentPrefix = null; if (indentLevel < 0) { indentPrefix = "«"; indentLevel = 0; } else if (indentLevel > 0) { // Use a cached pre-generated indent prefix for better performance while (indentStrings.Count <= indentLevel) { int newLevel = indentStrings.Count; string prefix = ""; for (int i = 1; i <= newLevel; i++) { prefix += "- "; } indentStrings.Add(prefix); } indentPrefix = indentStrings[indentLevel]; } FL.Text( prio, indentPrefix + shortName + ": " + shortMsg, (!string.IsNullOrEmpty(msg) ? msg + "\n\n" : "") + "Event ID: " + id + "\nEvent type: " + eventType + "\nSource: " + sourceName); if (eventType == TraceEventType.Start) { indentLevel++; } } }