Error logging is important in many applications for thorough troubleshooting. It is common for end users of an application to not remember what the error said exactly. Recording specific errors in a log enables you to get the specific error message without recreating the error.
While error logging is very important, you only want to use it to trap specific levels of errors because it carries overhead and can reduce the performance of your application. Specifically, log only errors that are critical to your application integrity - for instance, an error that would cause the data that the application is working with to become invalid.
There are three main approaches to error logging:
Write error information in a text file or flat file located in a strategic location.
Write error information to a central database.
Write error information to the system Event Log, which is available on all the versions of Windows supported by the .NET Framework 3.0. The .NET Framework includes a component that can be used to write and read from the System, Application, and Security Logs on any given machine.
The type of logging you choose depends on the categories of errors you wish to trap and the types of machines on which you will run your application. If you choose to write to the Event Log, you need to categorize the errors and write them in the appropriate log file. Resource-, hardware-, and system-level errors fit best into the System Event Log. Data access errors fit best into the Application Event Log. Permission errors fit best into the Security Event Log.
Three Event Logs are available: the System, Application, and Security Logs. Events in these logs can be viewed using the Event Viewer, which is accessed from the Control Panel. Access Administrative Tools and then select the Event Viewer subsection to view events. Typically, your applications would use the Application Event Log.
Event logging is available in your program through an EventLog component, which can both read and write to all of the available logs on a machine. The EventLog component is part of the System .Diagnostics namespace. The component allows adding and removing custom Event Logs, reading and writing to and from the standard Windows Event Logs, and creating customized Event Log entries.
Event Logs can become full, as they have a limited amount of space, so you only want to write critical information to your Event Logs. You can customize each of your system Event Log’s properties by changing the log size and specifying how the system will handle events that occur when the log is full. You can configure the log to overwrite data when it is full or overwrite all events older than a given number of days. Remember that the Event Log that is written to is based on where the code is running from, so if there are many tiers, then you must locate the proper Event Log information to research the error further.
There are five types of Event Log entries you can make. These five types are divided into event type entries and audit type entries.
Event type entries are as follows:
Information - Added when events such as a service starting or stopping occurs
Warning - Occurs when a noncritical event happens that might cause future problems, such as disk space getting low
Error - Should be logged when something occurs that prevents normal processing, such as a startup service not being able to start
Audit type entries usually go into the Security Log and can be either of the following:
Success audit - For example, a success audit might be a successful login through an application to an SQL Server.
Failure audit - A failure audit might come in handy if a user doesn’t have access to create an output file on a certain file system.
If you don’t specify the type of Event Log entry, an information type entry is generated.
Each entry in an Event Log has a Source property. The Source property is required and is a programmer-defined string that is assigned to an event that helps categorize the events in a log. A new Source must be defined prior to being used in an entry in an Event Log. The SourceExists method is used to determine whether a particular source already exists on the given computer. We recommend that you use a string that is relevant to where the error originated, such as the component’s name. Packaged software often uses the software name as the Source in the Application Log. This helps group errors that occur by specific software package.
The EventLog component is in the System.Diagnostics namespace. To use it conveniently, include an Imports System.Diagnostics statement in the declarations section of your code.
Important | Certain security rights must be obtained in order to manipulate Event Logs. Ordinary programs can read all of the Event Logs and write to the Application Event Log. Special privileges, on the administrator level, are required to perform tasks such as clearing and deleting Event Logs. Your application should not normally need to do these tasks, or to write to any log besides the Application Event Log. |
The most common events, methods, and properties for the EventLog component are listed and described in the following tables.
The following table describes the relevant event:
Event | Description |
---|---|
EntryWritten | Generated when an event is written to a log |
The following table describes the relevant methods:
Methods | Description |
---|---|
CreateEventSource | Creates an event source in the specified log |
DeleteEventSource | Deletes an event source and associated entries |
WriteEntry | Writes a string to a specified log |
Exists | Can be used to determine whether a specific event log exists |
SourceExists | Used to determine whether a specific source exists in a log |
GetEventLogs | Retrieves a list of all Event Logs on a particular computer |
Delete | Deletes an entire Event Log. Use this method with care. |
The following table describes the relevant properties:
Properties | Description |
---|---|
Source | Specifies the source of the entry to be written |
Log | Used to specify a log to write to. The three logs are System, Application, and Security. The System Log is the default if not specified. |
The following example illustrates some of these methods and properties:
Sub LoggingExample1() Dim objLog As New EventLog() Dim objLogEntryType As EventLogEntryType Try Throw (New EntryPointNotFoundException()) Catch objA As System.EntryPointNotFoundException If Not EventLog.SourceExists("Example") Then EventLog.CreateEventSource("Example", "System") End If objLog.Source = "Example" objLog.Log = "System" objLogEntryType = EventLogEntryType.Information objLog.WriteEntry("Error: " & objA.Message, objLogEntryType) End Try End Sub
The preceding code declares two variables: one to instantiate your log and one to hold your entry’s type information. Note that you need to check for the existence of a source prior to creating it. The following two lines of code accomplish that:
If Not EventLog.SourceExists("Example") Then EventLog.CreateEventSource("Example", "System")
Once you have verified or created your source, you can set the Source property of the EventLog object, the Log property to specify which log you want to write to, and EventLogEntryType to Information (other options are Warning, Error, SuccessAudit, and FailureAudit). If you attempt to write to a source that does not exist in a specific log, you get an error. After you have set these three properties of the EventLog object, you can then write your entry. In this example, you concatenated the word Error with the actual exception’s Message property to form the string to write to the log:
objLog.Source = "Example" objLog.Log = "System" objLogEntryType = EventLogEntryType.Information objLog.WriteEntry("Error: " & objA.Message, objLogEntryType)
As an alternative to the Event Log, you can write your debugging and error information to trace files. A trace file is a text-based file that you generate in your program to track detailed information about an error condition. Trace files are also a good way to supplement your event logging if you wish to track detailed information that would potentially fill the Event Log.
A more detailed explanation of the variety of trace tools and uses in debugging follows in the section “Analyzing Problems and Measuring Performance via the Trace Class,” but this section covers some of the techniques for using the StreamWriter interface in your development of a trace file.
The concepts involved in writing to text files include setting up streamwriters and debug listeners. The StreamWriter interface is handled through the System.IO namespace. It enables you to interface with the files in the file system on a given machine. The Debug class interfaces with these output objects through listener objects. The job of any listener object is to collect, store, and send the stored output to text files, logs, and the Output window. In the example, you will use the TextWriterTraceListener interface.
As you will see, the StreamWriter object opens an output path to a text file, and by binding the StreamWriter object to a listener object you can direct debug output to a text file.
Trace listeners are output targets and can be a TextWriter or an EventLog, or can send output to the default Output window (which is DefaultTraceListener). The TextWriterTraceListener accommodates the WriteLine method of a Debug interface by providing an output object that stores information to be flushed to the output stream, which you set up by the StreamWriter interface.
The following table lists some of the commonly used methods from the StreamWriter object:
Method | Description |
---|---|
Close | Closes the StreamWriter |
Flush | Flushes all content of the StreamWriter to the output file designated upon creation of the StreamWriter |
Write | Writes byte output to the stream. Optional parameters allow designation of where in the stream (offset). |
WriteLine | Writes characters followed by a line terminator to the current stream object |
The following table lists some of the methods associated with the Debug object, which provides the output mechanism for your text file example to follow:
Method | Description |
---|---|
Assert | Checks a condition and displays a message if False |
Close | Executes a flush on the output buffer and closes all listeners |
Fail | Emits an error message in the form of an Abort/Retry/Ignore message box |
Flush | Flushes the output buffer and writes it to the listeners |
Write | Writes bytes to the output buffer |
WriteLine | Writes characters followed by a line terminator to the output buffer |
WriteIf | Writes bytes to the output buffer if a specific condition is True |
WriteLineIF | Writes characters followed by a line terminator to the output buffer if a specific condition is True |
The following example shows how you can open an existing file (called mytext.txt) for output and assign it to the Listeners object of the Debug object so that it can catch your Debug.WriteLine statements:
Sub LoggingExample2() Dim objWriter As New _ IO.StreamWriter("C:\mytext.txt", True) Debug.Listeners.Add(New TextWriterTraceListener(objWriter)) Try Throw (New EntryPointNotFoundException()) Catch objA As System.EntryPointNotFoundException Debug.WriteLine(objA.Message) objWriter.Flush() objWriter.Close() objWriter = Nothing End Try End Sub
Looking in detail at this code, you first create a StreamWriter that is assigned to a file in your local file system:
Dim objWriter As New _ IO.StreamWriter("C:\mytext.txt", True)
You then assign your StreamWriter to a debug listener by using the Add method:
Debug.Listeners.Add(New TextWriterTraceListener (objWriter))
This example forces an exception and catches it, writing the Message property of the Exception object (which is Entry point was not found) to the debug buffer through the WriteLine method:
Debug.WriteLine(objA.Message)
Finally, you flush the listener buffer to the output file and free your resources:
objWriter.Flush() objWriter.Close() objWriter = Nothing