Although handling individual exceptions increases the usability of the system, only through thorough analysis of exception history can we hope to diagnose endemic architectural problems. Building a useful exception log involves two key decisions—what data we’ll store and where we’ll store it. In this article, we’ll look at those two issues.
Exception log entries
A good exception log captures all exceptions whether the user sees them or not. This allows the developer to examine how exceptions are generated over time and perhaps modify the way the application works to match the usage patterns. But the log entries are only as useful as the data captured. The first instinct may be simply to capture the information from the exception object itself, but this is a mistake. As much information as possible about the machine and application state is necessary as well. This information comes from capturing stack traces or using the System.Reflection namespace to acquire assembly information.
A thorough log starts with the date and time of the exception and all of the exception information (Source, StackTrace, etc.). The Environment namespace is used to capture the MachineName and the complete CallStack. Retrieve the name and version of the currently executing Assembly using System.Reflection. The AppDomain object returns the name of the application domain (FriendlyName) and the thread upon which it is executing (GetCurrentThreadID). Finally, always record the security context from the Thread.CurrentPrincipal object in the System.Threading namespace. Once all exception information is collected, it needs to be stored.
Log storage options
After exploring potential locations, I’m left with three contenders: a log file, the Windows event log, or a database. Let’s consider each of these potential locations.
A log file
The key advantage to using a custom log file is flexibility. The data format is determined, as well as where it’s stored and how it’s managed. The main reason most developers don’t choose this method is the responsibility for managing concurrent access to the log and developing the management tools required for sizing, reporting, backing up, and recovering the log. Given other options that provide this functionality automatically, the only reason to create a log file is if the target environment doesn’t support event logging or relational access; e.g., logging disconnected client exceptions. Another logging option involves the Windows event log.
Windows event log
Microsoft provides a central logging repository in all versions of Windows NT, Windows 2000, Windows XP, and Windows .NET Server. Microsoft uses the log to track operating system events (system log), events generated by installed applications (application log), and security information recorded for either system or application programs (security log). Although developers have always had the ability to either insert custom events in the application log or create a custom log, the .NET Framework’s support for log creation and management makes it simple to implement. The EventLog class in the System.Diagnostics namespace facilitates management of custom event logs.
The event log mechanism is highly reliable. It has its own management tools, allowing the developer to specify the size of the log and the lifetime of log entries, and it is monitored by most systems-monitoring tools. It’s also easy to compare the event log to the log entries captured by the system, application, and security logs to see if something else happening to the machine caused the exceptions. The biggest disadvantage of using the event logging mechanism is that events are always logged to the local machine. Although events can be written to the event log on another machine, this could create a problem if the source of the error comes from a failed machine or a failed network connection—the entry would never be recorded. Also, management of the collective logs is required, unless you use Microsoft’s Application Center Server or a third-party package to manage event log collection and reporting. The last logging option to consider is enlisting a relational database.
Using a centralized relational database eliminates the problems caused by the event log’s local repository design. Storing all exception log information in a central database repository allows remote reporting, management, and notification via triggers or database-monitoring tools. It also provides more flexibility in the storage and categorization of exception information. Adding Microsoft Message Queuing services to the error-logging solution increases the reliability of the logging mechanism and also removes any potential bottlenecks caused when the local system waits for exception-logging confirmation from the remote logging server.
The biggest drawback of the relational database approach is the need to develop custom management and reporting tools. Another disadvantage is the lack of access to the system, application, and security log entries on the machines executing the application, unless those log entries are manually imported for future analysis. Since Crystal Reports can use either event logs or relational tables as data sources, you can create management reports that combine information from both sources to use in logging reports.
Make the decision
Deciding where to store the exception logging data is not nearly as critical as deciding that it must be collected. Architects must realize that finishing an application is only half the battle. Applications must be designed so that they produce the information necessary to keep the applications running smoothly for years after they’re put into production. Exception logging is one of the key design points to consider in any large-scale application.