I guess this might be kind of a belated rant… some will know what i’m talking about. At a previous job, we had a need to log stuff: errors, debug info, etc. We were given an API by the upper programmers and it was, shall we say, less than excellent. It was based on some MS application block, which had dependancies on a bunch of other application blocks and made your config files massive. But, boy was it configurable. Blah. The most difficult part was writing out the signature of the method and the parameters. We couldn’t consider doing the values passed in yet. Every time we changed the method signature, we had to alter all the logging statements. We usually ignored logging until late in development when the methods would stabilize.
So I proposed a more simple way. I mean, Reflection is supposed to know all this stuff, right? Why not have it interrogate the current method and pass it and its parameters somewhere to be logged, or at least generated. It was declined. I didn’t take it personally, but inside I was using an evil villain voice: "you fools!"
So when I left, I took my ideas with me and quickly built this into my new application suite. It’s not as wicked-cool as having a config file that can switch between text file and sql logging, in fact, it has NO config file. The simplicity of the logging is the key.
So let’s say you want to log an error. This is all you write:
Try Catch ex As Exception Logging.LogError(ex, System.Reflection.MethodInfo.GetCurrentMethod) End Try
And it’s the same for every method. That right there is worth its weight in code. But what about when you have parameters? You would need to capture them too. The code is almost unbearable at that point, right?
Try Catch ex As Exception Dim errorParms As New System.Collections.Specialized.StringCollection With errorParms .Add(parm1) .Add(parm2.ToString) End With Logging.LogError(ex, System.Reflection.MethodInfo.GetCurrentMethod, errorParms) End Try
So what’s the error log look like? It’s tab-delimited and has the basics of what you need:
DATE USER METHOD ERROR MESSAGE
4/16/2008 9:04:30 PM 700CB\anachostic WindowsApplication1.Form1.LogWithoutParameters(parm1,parm2) This is a generated error.
4/16/2008 9:04:30 PM 700CB\anachostic WindowsApplication1.Form1.LogWithParameters(parm1=This is parm 1,parm2=12345) This is a generated error.
Have I sold it yet? Or at least given you a starting point for your own logging "framework"? Ahem. Please, it’s just a simple class. Here it is for your plunder:
Imports System.Reflection Imports System.Collections.Specialized Public Class Logging Public Shared LOGFILE_NAME As String Shared Sub New() LOGFILE_NAME = "c:\" & My.Application.Info.ProductName & "-Errors.log" End Sub Shared Sub LogError(ByVal ex As Exception, ByVal method As MethodBase) LogError(ex, method, Nothing) End Sub Shared Sub LogError(ByVal ex As Exception, ByVal method As MethodBase, _ ByVal parameterValues As StringCollection) Dim sb As New System.Text.StringBuilder Try With sb .Append(Now.ToString) .Append(vbTab) .Append(My.User.CurrentPrincipal.Identity.Name) .Append(vbTab) If method IsNot Nothing Then .Append(method.ReflectedType.FullName & "." & method.Name) .Append("(") For i As Integer = 0 To method.GetParameters.Length - 1 .Append(method.GetParameters(i).Name) If parameterValues IsNot Nothing _ AndAlso i < parameterValues.Count Then .Append("=" & parameterValues(i).Replace(vbCrLf, "<CR>")) End If .Append(",") Next If sb.ToString.EndsWith(",") Then sb.Length -= 1 .Append(")") End If .Append(vbTab) If ex IsNot Nothing Then .Append(ex.Message.Replace(vbCrLf, "<CR>")) End If End With Catch exc As Exception sb.Append("<ERROR WHILE PARSING: " & exc.Message & ">") End Try LogMessage(sb.ToString) End Sub Shared Sub LogMessage(ByVal msg As String) Try If Not IO.File.Exists(LOGFILE_NAME) Then IO.File.WriteAllText(LOGFILE_NAME, "DATE" _ & vbTab & "USER" _ & vbTab & "METHOD" _ & vbTab & "ERROR MESSAGE" _ & vbCrLf) End If IO.File.AppendAllText(LOGFILE_NAME, msg & vbCrLf) Catch ex As Exception End Try End Sub End Class