Adding Event Logging to Your Programs

Adding Event Logging to Your Programs

In keeping with the diagnostics flavor of this chapter, let's see how easy it is to add event logging to a program. If you are running Windows 98, Windows NT 4, Windows Me, Windows 2000, or a prerelease version of Windows XP, you can easily add event-logging features to your programs. You can then see information about the events you log in the Event Viewer, which you open from the Administrative Tools window in Control Panel.

The Event Viewer is used by professional applications to log activity, especially any rogue activity such as errors, the absence of disk space or memory, and so on. We can use our Test Error Program to see how various events could be logged, as shown in Figure 7-26.

Figure 7-26

The Event Viewer lets you log events in your programs.

Double-clicking on the highlighted error will display that event's properties. The properties include the date, time, and source of the event, as well as a description of what happened, as you can see in Figure 7-27. Because of the power of the .NET Framework, adding this professional touch to your program simply takes a few lines of code.

Figure 7-27

Event details.

The Philosophy of Logging Events to the Event Viewer

Event logging in the latest versions of Microsoft Windows provides a standard, centralized way for your Visual Basic .NET applications to record important software and hardware events. For example, when an error occurs, the system administrator or a support technician must determine what caused the error, attempt to recover any lost data, and prevent the error from recurring. As you can imagine, it's helpful if not only applications but also the operating system and other system services record important events. These events might be occurrences such as low-memory conditions or failed attempts to access a disk. The system administrator can use the event log to help determine what conditions caused the error and the context in which it occurred.

The Windows Event Viewer is a standard user interface for viewing these event logs and also a programming interface for examining log entries. In Visual Basic .NET, you use the EventLog class, which permits you to easily connect to event logs on both local and remote computers and write entries to these logs. You can also read entries from existing logs and create your own custom event logs.

An event, as defined in Windows, is any significant occurrence—whether in the operating system or in an application—that requires users to be notified. Critical events are sent to the user in the form of an immediate message on the screen. Other event notifications are written to one of several event logs that record the information for future reference. Every event log entry is classified into one of the following categories: errors, warnings, information, success audits, or failure audits.

Basically, three event logs are available by default on computers running Windows XP, Windows 2000 or Windows NT 4:

  • System log, which tracks various events that occur on system components, such as drivers

  • Security log, which tracks security changes and possible breaches

  • Application log, which tracks events that occur in a registered application, such as the Test Error Program.

In addition to these logs, application services such as Active Directory can create their own default logs. Visual Basic .NET gives you the ability to create your own custom logs using classes in the System.Diagnostics namespace.

note

Event logs are installed as part of Windows. You must have either Windows XP, Windows NT version 4, or Windows 2000 installed on the computer on which you do your development work in order to create and test the EventLog object.

What to Log as an Event

As you can see, we want to log only major events to the Event Viewer, but we can be more liberal with tracing. You usually only want to spawn messages to the Event Viewer that reflect computer resource problems. For example, if an application enters a low-memory situation that degrades its performance, logging a warning event when memory allocation fails might provide a clue about what went wrong. You also want to consider logging certain information events. For example, a server-based application might use a database to record a user logging on, opening a database, or starting a file transfer. The server can also log error events it encounters such as the inability to access a file, loss of a connection, file corruptions, or FTP success or failure messages. Be judicious about any event sent to the viewer.

Event logging does consume resources such as disk space and processor time. The amount of disk space that an event log requires and the overhead for an application that logs events depend on how much information you choose to log. Therefore, it is important to log only essential information. It is also best to place event log calls in an error path in the code rather than in the main code path, as we did in our example, so as to not reduce performance. Also, if you send several useless messages to the event viewer, not only does that crowd out messages from other applications, but it also creates a false equality—if every message is important then no message is important. In other words, choose your event messages wisely, Grasshopper.

Adding Event Logging to the ErrorTrace.vb Class

Open the code module that contains the ErrorTrace class, and add a private class member that will hold a reference to the EventLog object. (Instead of using the DLL I created earlier, I copied the ErrorTrace class file to this project's folder and then added the class to the project.)

Public Class ErrorTrace     Private m_errorFile As StreamWriter     Private m_fileListener As TextWriterTraceListener     Private m_sAppName As String = ""     Private m_tsTraceSwitch As TraceSwitch     Private m_elEventLog As EventLog

    Private m_isEventWriter As Boolean

Because we need to add only a few additional lines to our constructor, we will modify its signature instead of overloading it. The ratio of new lines to the size of the constructor is so small that this is the best design choice. If the user sends True as a parameter for the addEventWriter variable, our code adds a new EventLog. Because the variable m_elEventLog is defined with class scope, we can use it anywhere in the class.

Public Sub New(ByVal sApplication As String, _     ByVal addEventWriter As Boolean)     Dim sEnv As String =  _         Environment.GetEnvironmentVariable("demoSwitch")     m_tsTraceSwitch = New TraceSwitch("demoSwitch",  _         "Testing the TraceSwitch")     m_tsTraceSwitch.Level = CInt(sEnv)     If m_tsTraceSwitch.Level = TraceLevel.Off Then Exit Sub     m_errorFile = File.AppendText("C:\ErrorFile.txt")     m_fileListener = New TextWriterTraceListener(m_errorFile)     Trace.Listeners.Add(m_fileListener)     m_sAppName = sApplication     m_isEventWriter = addEventWriter 'used when disposing     '-- Do we want to log info to the event log also?     If (m_isEventWriter = True) Then         m_elEventLog = New EventLog("Application", ".", _             sApplication)         m_elEventLog.WriteEntry(Date.Now & " Application: " & _             m_sAppName & " started", EventLogEntryType.Information)     End If     '-Write information to our file –    Trace.WriteLine(Date.Now & " Application: " & m_sAppName & _         " started")     Trace.WriteLine("Application Version: " & _         Environment.Version.ToString)     Trace.WriteLine("Current Directory: " & _         Environment.CommandLine)     Trace.WriteLine("Machine Name: " & Environment.MachineName)     Trace.WriteLine("Operating System: " & _         Environment.OSVersion.ToString)     Trace.WriteLine("User: " & Environment.UserName.ToString)     Trace.WriteLine("------------------------------------------") End Sub

If the user wants certain events to be logged in the Event Viewer, True is placed in the second parameter of the constructor. If this value is used, we then create a new instance of the EventLog object. We pass in three parameters to the EventLog constructor:

  • The log file that the events will write to. Recall that we looked in the Application Log on the left side of the viewer.

  • The machine to log events from. The period (.) provides the default for the current machine. However, you could specify any other machine, such as a web server.

  • The description that will show up in the Source column of the event. Traditionally, the application posting the message places its name in this column.

Then we write a simple message stating the time the application started. The WriteEntry method is overloaded and permits each message to have a category type, such as Information. You'll see in a minute when we write an error that we designate the message as category type Error and the appropriate icon is displayed for us.

If (addEventWriter = True) Then

    m_elEventLog = New EventLog("Application", ".", _

        sApplication)

    m_elEventLog.WriteEntry(Date.Now & " Application: " & _

        m_sAppName & " started", EventLogEntryType.Information)

End If

We could have added our m_elEventLog to the Listeners collection of the Trace object, using a statement such as this:

Trace.Listeners.Add(m_elEventLog)

However, if we did this, any message that was sent to the trace code based on the TraceSwitch would also be sent to the event log. As you can imagine, this flood of messages could quickly fill the event log. However, if you ever need to do this, no more code is necessary. All messages printed with Trace would send all messages to the listeners, which in our case would be the text file and the event log. As a design decision, we decided to keep trace statements and event logs separate. But in doing so, we have to add a few specialized procedures to accept messages to be logged.

One event we definitely want to log is an error. Simply add this line to the writeError procedure. Because this event is an error, pass in the Error enumerated category type for the EventLogEntryType, which will display the red ball with the X.

Public Sub writeError(ByVal eException As System.Exception)            Trace.WriteLineIf(m_tsTraceSwitch.TraceInfo, _         "Error: " & eException.Message)     Trace.WriteLineIf(m_tsTraceSwitch.TraceVerbose, _         "Stack Trace: ")     Trace.WriteLineIf(m_tsTraceSwitch.TraceVerbose, _         eException.StackTrace)     Trace.WriteLineIf(m_tsTraceSwitch.TraceVerbose, _         "------------------------------------")

    'Write any error to the event log

    If (m_isEventWriter = True) Then

        m_elEventLog.WriteEntry(eException.Message, _

            EventLogEntryType.Error)

    End If End Sub

Now add the following two procedures to the class. As you can see, one is used for logging an informational message, and the other for a warning message. The Information and Warning enumerated values are already included, so it's painless to use these procedures from outside the class. Of course, adding the code above already performs the logging of an error.

Public Sub writeEventLogInformation(ByVal sMessage As String)     m_elEventLog.WriteEntry(sMessage, _         EventLogEntryType.Information) End Sub Public Sub writeEventLogWarning(ByVal sMessage As String)     m_elEventLog.WriteEntry(sMessage, _         EventLogEntryType.Warning) End Sub

Finally, when our class is disposed of, we are going to write a message to the event log indicating that the program has terminated. By interrogating the Boolean member variable m_isEventWriter, we can determine whether the event log is active. If it is, we can write the informational message to it.

Public Sub dispose()     If m_tsTraceSwitch.Level = System.Diagnostics.TraceLevel.Off Then         Exit Sub     End If     Trace.WriteLine(Date.Now & "  Application: " & _         m_sAppName & " ended")     If (m_isEventWriter = True) Then        m_elEventLog.WriteEntry(Date.Now & " Application: " & _             m_sAppName & " ended.", _            EventLogEntryType.Information)    End If     m_errorFile.Flush()     m_errorFile.Close() End Sub

Using Our New Event Logging Capability

To see how our new event logging capability works, we'll again use the code that generates the divide-by-zero error. First, the signature for the constructor changes, so we have to add a second Boolean parameter to tell our class whether we want to use error logging.

Imports System.Text Imports CtrlChrs = Microsoft.VisualBasic.ControlChars Public Class Form1     Inherits System.Windows.Forms.Form     Dim sErrorMessage As New StringBuilder()     Dim etErrorTracing As _        New Errors.ErrorTrace("Test Error Program", _        True)

Now the user of our class can see that the parameter takes either True or False for the addEventWriter parameter. As you can see in Figure 7-28, naming parameters with useful names can make using our class a breeze.

Figure 7-28

Descriptive parameter names simplify the use of our class.

Because we are already writing an error to the event log, we don't need to change the writeError method. Recall that our class also logs any error to both the file and the event log automatically.

Private Sub procedure1()     Try         sErrorMessage.Append("In procedure1, calling " & _             "procedure2" & CtrlChrs.CrLf)         procedure2()     Catch e As Exception         etErrorTracing.writeError(e)         sErrorMessage.Append("Catch block in procedure1: " & _             e.ToString & CtrlChrs.CrLf)     Finally         sErrorMessage.Append("Finally in procedure1" & _             CtrlChrs.CrLf)         MessageBox.Show(sErrorMessage.ToString, "Error Stack _         Example", MessageBoxButtons.OK, MessageBoxIcon.Information)     End Try End Sub

Now, anywhere we want to add an event for either information or warning purposes, we can simply choose the correct method, as you can see in Figure 7-29. Our class will handle the behind the scenes details.

Figure 7-29

Our class makes it easy to add information and warning messages.



Coding Techniques for Microsoft Visual Basic. NET
Coding Techniques for Microsoft Visual Basic .NET
ISBN: 0735612544
EAN: 2147483647
Year: 2002
Pages: 123
Authors: John Connell

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net