Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

World's easiest Trace function

0.00/5 (No votes)
3 Mar 2005 1  
Add tracing capability to your app with one easy function

Introduction

Debugging your way through code can be boring and tiring. When you're hunting down a bug, it helps if you can zero right in on the problem. By adding a simple trace file to your app, you can pinpoint the exact sub or function where things went wrong.

With a bit more work, you can configure your app to turn on or turn off writing to the trace file as needed. You can also start with a fresh trace file every time your app is loaded.

With the trace mechanism in place, you can run your app until things go wrong. Then by examining the trace file, you know just where to turn in your code.

This simple approach uses .NET's StackTrace feature to keep track of where users have been as they work with your app.

Discussion

StackTrace is part of the System.Diagnostics namespace, so you will want to include a reference to that in your project with an Imports statement at the beginning of your class or module.

StackTrace is a pile of StackFrames. Each StackFrame holds information about each function or sub as your application accesses it. Every time a function or sub is accessed, information in the form of a StackFrame about it is pushed onto the top of the StackTrace. This information is available as a string, which we can easily parse to extract just the information we want.

There are two parts to using this mechanism: writing a short subroutine to write material to the trace file, and adding a line to each sub or function calling that subroutine. Let's look at the subroutine first. I've simplified the code somewhat to make it easier to follow, and I use some constants that I've defined elsewhere; their values should be obvious.

 Friend Sub Tracer()
        Dim texttoadd As String
        Dim logtext() As String
        Dim fileline() As String
        Dim fs As StreamWriter
        Dim strace As New StackTrace(True)
        Try
            If Not File.Exists(TRACE_LOG) Then
                fs = File.CreateText(TRACE_LOG)
                fs.Write("Trace Log " & Format(Now) & CR & CR)
                fs.Flush()
                fs.Close()
            End If
            logtext = strace.GetFrame(1).ToString.Split(SPACE)
            fileline = logtext(6).Split(BACKSLASH)
            Dim i As Integer = fileline.GetUpperBound(0)
            texttoadd = logtext(0) & COLON & SPACE & _
                 fileline(i).Substring(0, fileline(i).Length - 2)
            fs = File.AppendText(TRACE_LOG)
            fs.WriteLine(texttoadd)
            fs.Flush()
            fs.Close()
        Catch ex As Exception
            MsgBox(ex.ToString)
        End Try

    End Sub

The StreamWriter is the mechanism used to write the information to the log file. If you are unfamiliar with writing streams, check out the .NET documentation on the topic.

The StackTrace is created using the optional True parameter to indicate that we want the trace to include information about the file the function or subroutine belongs to. The nice thing about the StackTrace is that it holds information about everything that has already happened in the application. In fact, the information we want is not even on the top of the stack--it's one item down, because the last item pushed onto the stack is the call to our Tracer() routine. That's why when we capture the StackFrame we want, we have to get the second item (in a zero-based array).

We can confidently split the StackFrame to an array because these entries always have the same format. The first element in the array will contain the name of the sub or function we want to capture. The last element will have the name of the source file as well as the line number and offset. A typical logtext array will look something like this:

(0): "myFunction"
(1): "at" 
(2): "offset" 
(3): "87" 
(4): "in" 
(5): "file:line:column"
(6): "C:\ProgramFiles\VS\MyProject\MyModule.vb:12345:67

We can then confidently split the last element of logtext into an array, using the backslash as the delimiter. We only care about the last element of that array. Note, though, that the last element actually ends with a line-end character, so we must parse that away before we write to our trace file. (If you don't parse it away, your trace file will end up double-spaced, which you might prefer.)

The second part of the setup is easy: Just add a call to Tracer() in every function or sub you want to include in your trace file:

Private Sub DoSomething(somethingToDo as String)
   Tracer()
   somethingToDo = somethingToDo & somethingElse
   ...
End Sub

To turn the trace on and off at will, you can add a global Boolean variable to your code, and then examine the state of that variable at run-time to decide if you are tracing or not. The easiest way to do this is to pass a command-line argument:

Class MyMainForm
Public traceIt as Boolean = False

Public Sub Main()
   If Environment.CommandLine.IndexOf("/t")> 0 then traceIt = True

   ...
End Sub

Private Sub DoSomething(somethingToDo as String)
   If traceIt Then Tracer()
   somethingToDo = somethingToDo & somethingElse
   ...
End Sub

Finally, to start a new trace file every time you start your app, add a line to the main program block to delete the existing file:

If File.Exists(TRACE_LOG) then File.Delete(TRACE_LOG)

You can apply the same technique in a Try/Catch block as well, and incorporate the exception in the trace file. But I'll leave that for you to play with.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here