Handling and dealing with Errors can be tricky. When your program crashes you want to fix it as soon as possible. One of the most valuable pieces of information- when the Error is generated from a .NET Program- is the Exception type as well as the stack trace, which can be used to try to determine the cause of the problem.
More prudently, of course, it makes sense to also log data. My personal approach is to implement a relatively simple class that will write log files to the Application Data folder, and overwrite old logs. It is implemented as a TraceListener, and merely needs to be invoked to start capturing Debug output to a file. The advantage here is that the Debug statements are removed in a release build; the downside is that the Debug statements are removed from a release build. Using the Trace class instead of the Debug class for writing debug output will allow that trace output to be captured by the “DebugLog” class, listed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
using System; using System.Diagnostics; using System.IO; using System.Management; using System.Reflection; namespace BASeCamp.Debugging { public class DebugLogger : TraceListener { public static bool EnableLogging = true; public static DebugLogger Log = new DebugLogger(Assembly.GetCallingAssembly().GetName().Name); private String _LoggerName; private StreamWriter LogStream = null; private void InitLog() { PurgeOldLogs(); String BasePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "BASeCamp\\DebugLogs", _LoggerName); Directory.CreateDirectory(BasePath); String LogFileUse = Path.Combine(BasePath, DateTime.Now.ToString("yyyy-MM-dd-hh-mm-ss") + ".log"); LogStream = new StreamWriter(new FileStream(LogFileUse, FileMode.Create)); LogStream.WriteLine("--Log Initialized--"); WriteLogHeader(); } private void WriteLogHeader() { LogStream.WriteLine("--" + DateTime.Now.ToString() + "--"); LogStream.WriteLine("=== System Information ==="); try { var searcher = new ManagementObjectSearcher( "SELECT * FROM " + "win32_operatingsystem"); foreach (ManagementObject wmi_HD in searcher.Get()) { PropertyDataCollection searcherProperties = wmi_HD.Properties; foreach (PropertyData sp in searcherProperties) { LogStream.WriteLine(sp.Name + " = " + sp.Value); } } LogStream.WriteLine("=== End System Information ==="); } catch (Exception exx) { LogStream.WriteLine("Exception retrieving System Info:" + exx.ToString()); } } private static TimeSpan AgeCutoff = new TimeSpan(7, 0, 0, 0); //two weeks. private void PurgeOldLogs() { String BasePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "BASeCamp\\DebugLogs", _LoggerName); if (!Directory.Exists(BasePath)) return; var getdir = new DirectoryInfo(BasePath); foreach (var iterate in getdir.GetFiles("*.log")) { if ((DateTime.Now - iterate.LastWriteTime) > new TimeSpan(14, 0, 0, 0)) { try { iterate.Delete(); } catch (IOException exx) { } } } } public override void Write(String LogMessage) { LogStream.WriteLine(DateTime.Now.ToString("hh-mm-ss") + ">>" + LogMessage); LogStream.Flush(); } public override void WriteLine(string message) { Write(message + Environment.NewLine); } public DebugLogger(String sName) { _LoggerName = sName; InitLog(); if (EnableLogging) Debug.Listeners.Add(this); //Trace.Listeners.Add(this); } } } |
The debug logs captured can be exceedingly useful- a more full implementation of a Debug handler, or unhandled exception handler, could very well zip up some of those files and E-mail them to an appropriate Support address.
But what about Exceptions that occur that you don’t catch? Sometimes you simply don’t catch all your exceptions- and it’s reasonable, having a catch-all Exception handler can cause just as many unexpected issues as catching only those exceptions you know how to handle. Thankfully, all .NET Exceptions are actually logged to the Event Viewer.
The information is therefore available; but guiding a client or customer through the process of wandering through the Event Log is a bit much. So it makes perfect sense to let an Application do this job for you. Thankfully, .NET has a rich capability in terms of inspecting the Event Log. I Created a quick Application to demonstrate (Includes source code).
The principles exercised in the project could easily be used to create a zip or other archive filled with useful diagnostic information in the case of an unexpected crash; that zip could even be automatically E-mailed to support (as I mentioned above). With such a wealth of information it can help eliminate a lot of time-wasting back-and-forth trying to determine the cause of a problem. If nothing else, something like this Application would make finding the proper information slightly easier (with the appropriate tweaks to make the Stack Trace visible).
Have something to say about this post? Comment!