Somehow I’ve managed to never write about my Debug log class, looking through my post history.
It’s a versatile Debug Logging class that effectively hooks into and allows tracing of Trace.Write and Debug.Write method call output. I use it to redirect all Debug.Print() output to a text file for later examination. Some of my original considerations included being easy to use and add to existing software programs, as well as being able to do it’s job with as little interaction as possible. I’ve been able to use this successfully in it’s original program (BASeBlock) as well as inserting and using the class for Debug log functionality in software created and maintained as part of my Job, and it has worked wonderfully.
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 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
using System; using System.Diagnostics; using System.IO; using System.Management; using System.Net.Mime; using System.Reflection; namespace TestDebug { /// <summary> /// Debug logging output class. Can automatically transform Debug output (Debug.Print) and save it to a properly dated and organized /// file in %APPDATA%\Company\DebugLogs\AppName. Just make sure it get's initialized by calling it in some fashion- setting EnableDebugging to True is usually good enough. /// Note that Debug output will not appear for Release builds, if we ever use them. /// </summary> public class DebugLogger : TraceListener { public static bool EnableLogging = true; public static bool FullExceptionLogging = false; public static DebugLogger Log = new DebugLogger(Assembly.GetEntryAssembly().GetName().Name); private static String CompanyName; private String _LoggerName; private StreamWriter LogStream = null; private String _ActiveLogFile; public String ActiveLogFile { get { return _ActiveLogFile; } } object logStreamLock = new object(); private void InitLog() { FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location); CompanyName = fvi.CompanyName; if (string.IsNullOrEmpty(CompanyName)) CompanyName = "DebugLogging"; InitLog(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), CompanyName)); } private void InitLog(String sLogFolder) { PurgeOldLogs(); String BasePath = Path.Combine(sLogFolder, _LoggerName); Directory.CreateDirectory(BasePath); String LogFileUse = Path.Combine(BasePath, DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss-ffffff") + "." + (new Random().Next()).ToString("x8") + ".log"); FileStream fs = new FileStream(LogFileUse, FileMode.CreateNew); try { LogStream = new StreamWriter(fs); _ActiveLogFile = LogFileUse; } catch { fs.Dispose(); throw; } lock (logStreamLock) { LogStream.WriteLine("--Log Initialized--"); WriteLogHeader(); } } private void WriteLogHeader() { FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(Assembly.GetEntryAssembly().Location); LogStream.WriteLine("--" + DateTime.Now.ToString() + "--"); LogStream.WriteLine(fvi.ProductName); LogStream.WriteLine(fvi.LegalCopyright); LogStream.WriteLine("Main executable file: " + Assembly.GetEntryAssembly().Location); LogStream.WriteLine("=== System Information ==="); try { using (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), CompanyName,"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(64, 0, 0, 0)) { try { iterate.Delete(); } catch (IOException exx) { // } } } } bool writerecursion = false; public override void Write(String LogMessage) { if (writerecursion) return; writerecursion = true; try { lock (logStreamLock) { try { LogStream.Write(DateTime.Now.ToString() + ">>" + LogMessage); LogStream.Flush(); } catch (Exception exx) { //unknown error } } } finally { writerecursion = false; } } public override void WriteLine(string message) { Write(message + Environment.NewLine); } public DebugLogger(String sName, String sLogFolder) { _LoggerName = sName; InitLog(sLogFolder); if (EnableLogging) Debug.Listeners.Add(this); } public DebugLogger(String sName) { _LoggerName = sName; InitLog(); if (EnableLogging) { Debug.Listeners.Add(this); } AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; AppDomain.CurrentDomain.FirstChanceException += CurrentDomain_FirstChanceException; } void CurrentDomain_FirstChanceException(object sender, System.Runtime.ExceptionServices.FirstChanceExceptionEventArgs e) { if (!FullExceptionLogging) return; try { this.WriteLine(e.Exception.ToString()); } catch (Exception exx) { //Application must be in a very bad state- or, the error was actually caused by DebugLogger. } } void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { //Log the Exception. Note that we don't want ANOTHER exception to occur, so we wrap it all in a try-catch. try { this.WriteLine(e.ExceptionObject.ToString()); } catch (Exception exx) { //Application must be in a very bad state- or, the error was actually caused by DebugLogger. } } } } |
Example usage is straightforward- you enable logging by simply accessing the “EnableLogging” property; typically I set it to true for obvious reasons. The Static initializer of the class sets everything up from there:
1 2 3 4 5 6 7 8 |
class Program { static void Main(string[] args) { DebugLogger.EnableLogging = true; Debug.Print("Test!"); } } |
And BAM! We’ve got a fully functional debug log- after the debug logs become a week old, they get purged, too! How is that for convenience?
Have something to say about this post? Comment!