private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { if (!Loggers.IsUsed(this)) { return; } //Can be safely cast to Exception if RuntimeCompatibilityAttribute(WrapNonException=true) has been applied to your assembly, //which is automatically done for you since .net 2.0. 'Non exceptions' will be wrapped in RuntimeWrappedException. var ex = e.ExceptionObject as Exception; var frame = GetApplicableFrame(ex); string sourceFile = frame.GetFileName(); string description = "An unhandled exception occured and killed the application!"; if (sourceFile == null) { description += " SourceFile and Line are not available because the application does not have (up-to-date) .pdb files included in the folder holding the binaries where the exception occured."; } //Do not notify in write exceptions, they will not be handled anyways. LogWriteException = null; //_backgroundWorkQueue is not used because its background thread gets disposed if the application halts. However, we dispose it first so there is absolutely no chance that LogEntryWriteCallback is called by 2 threads at the same time. _backgroundWorkQueue.Dispose(); //Create a readable watson buckets. (_watsonBuckets holds a byte array.) //Watson buckets are somewhat handy for when there is no stacktrace available, or you cannot trace into third-party assemblies. //Only applicable with unhandled exceptions. //I did not simply replace the value for _watsonBuckets in the exception, because the Windows Error Report tool would not work: //You can get the path to the minidump there. The minidump can be analyzed using a tool like WinDbg. //Downside: The output contains a serialized byte array. //http://msdn.microsoft.com/en-us/library/ms404466(v=VS.110).aspx //http://mrpfister.com/programming/demystifying-clr20r3-error-messages/ //http://mrpfister.com/programming/opcodes-msil-and-reflecting-net-code/ ReadableWatsonBucketParameters readableWatsonBucketParameters = null; FieldInfo watsonBucketsField = ex.GetType().GetField("_watsonBuckets", BindingFlags.Instance | BindingFlags.NonPublic); if (watsonBucketsField != null) { byte[] arr = watsonBucketsField.GetValue(ex) as byte[]; if (arr != null) { var str = MarshalBytesToStruct <BucketParameters>(arr); readableWatsonBucketParameters = new ReadableWatsonBucketParameters(str); } } LogEntryWriteCallback(Level.Fatal, description, ex, new object[] { sender }, frame.GetMethod().Name, sourceFile, frame.GetFileLineNumber(), LogToDebug, readableWatsonBucketParameters); }
private void LogEntryWriteCallback(Level level, string description, Exception exception, object[] parameters, string member, string sourceFile, int line, bool logToDebug, ReadableWatsonBucketParameters readableWatsonBucketParameters) { if (level < CurrentLevel) { return; } //We are working around the problem that stuff closely linked to the gui /unknown stuff in another program cannot be serialized or deserialized. string[] parameterStrings = null; if (parameters != null) { parameterStrings = new string[parameters.Length]; for (int i = 0; i != parameters.Length; i++) { object parameter = parameters[i]; if (parameter != null) { parameterStrings[i] = parameter.ToString(); } } } var entry = new Entry { Timestamp = DateTimeOffset.Now.ToString("o"), Level = level, Description = description, Exception = exception, ReadableWatsonBucketParameters = readableWatsonBucketParameters, Parameters = parameterStrings, Member = member, SourceFile = sourceFile, Line = line }; string json = JsonConvert.SerializeObject(entry, Formatting.None, _jsonSerializerSettings); if (logToDebug) { Debug.WriteLine(json, "LOGGER"); } InvokeWriteLogEntry(entry, json); // Also handles write exceptions and invokes LogEntryWritten. }