Creating a custom error handling class for logging errors in ASP .NET can reduce the amount of code needed to log errors in each of your pages or functions. By using my custom class, I reduced the code needed in each event handler or method to a single line of code.
The class writes logs to the system EventLog, a database, a text file, or any combination of the three, depending on how it’s configured. Let’s dive in and see what’s needed to make this work.
Global.asax
I wanted this class to be usable in any web application, regardless of where that app was deployed. However, I may not always be able to write to the EventLog, or maybe I won’t have a database for an application. I may not have file permissions to write to a file, either. Instead of commenting out code, I used boolean constants to turn on and off each logging routine. These constants are defined and set in the Global file like this:
public static boolLogErrorToDatabase = true;
public static boolLogErrorToFile = false;
public static bool LogErrorToEventLog = false;
The Custom Error Class
The following class is attached for download and license-free usage: error-custom-class-code. Since you can view it in its entirety there, I’ll only post the most relevant text here to save space.
To access the EventLog, you need to use the System.Diagnostics namespace by placing a using statement at the top of the class. We also need the System.IO one for writing a text file:
using System.Diagnostics;
using System.IO;
Next comes the actual method we’ll provide to the rest of the application. To do so, we make it public and use the void keyword to indicate the method returns nothing.
We’re also accepting 4 parameters. First is the Exception followed by the screen and event or method where the error occurred. Finally we’re saving the user’s name. These can be changed or added to in order to fit your own business needs.
public voidLogError(Exception ex, stringsScreen, stringsEvent, stringsUsername)
{
//code discussed below
}
Logging the Error
Inside the LogError method, we’re going to provide three separate functions: one each for logging to the EventLog, the database, and to a file. Since any combination of the three may be desired, we’re evaluating the global variables we set earlier.
Writing to the EventLog
if (Global.LogErrorToEventLog == true)
{
// Write error to log file.
stringsAppName = sScreen + ” :: “+ sEvent;
EventLogoEventLog = new EventLog();
if (!EventLog.SourceExists(sAppName))
{ EventLog.CreateEventSource(sAppName, “Ellefson Consulting Log”); } string sMessage = ex.ToString();
//log the entry
oEventLog.Source = sAppName;
oEventLog.WriteEntry(sMessage, EventLogEntryType.Error);
}
Looking at the code, we can see that we’re combining the sScreen and sEvent parameters into the sAppName variable that will be used in the EventLog. We then use this to see if the EventSource is registered in the computer’s registry and if not, register it. This is not something you otherwise need to worry about, but this check must be done to avoid an error in the event the source is not registered.
The log we want to write to will be called “Ellefson Consulting Log”, which will be created if it doesn’t exist. The rest is self-explantory.
When you go to Computer Management and view the Event Log, this is what you will see:

Error Log Image
Writing to a Database
After the EventLog code, we have another block that writes to a database, which is SQL Server 2005 in this case. Here I’m using the SQLHelper file to encapsulate my data access object usage, but any data connection code will work. In my database is a stored procedure called “uspLogError”, which accepts the same parameters as the other code here, writing the results to a table.
if (Global.LogErrorToDatabase == true)
{
intiErrorID = Convert.ToInt32(SqlHelper.ExecuteScalar(Global.ConnectionString, “uspLogError”, ex.ToString(), sScreen, sEvent, sUsername));
}
Writing to a File
Finally, the code writes to a text file. In this sample, the file is located in the C directory but you may want to put it somewhere else. The “true” option indicates we want to append to the file if it exists so we don’t lose the record of past errors. Without this the file would be overwritten. I’ve separated the parameters onto their own lines to make the text file more readable.
if (Global.LogErrorToFile == true)
{
// create a writer and open the file
TextWritertw = new StreamWriter(“C:\\ErrorLog.txt”, true);
// write a line of text to the file
tw.WriteLine(“Error: “+ ex.ToString());
tw.WriteLine(“Error location: “+ sScreen + sEvent);
tw.WriteLine(“Username: “ + sUsername);
tw.WriteLine(“Application: “ + Global.ApplicationName);
// close the stream
tw.Close();
}
Using the Class
At the top of my example .aspx page, I need a using statement to reference the custom class I’ve created. Since my application’s namespace is EllefsonConsulting and I’ve created my class in the Business Logic Layer folder I set up, my using statement looks like this:
using EllefsonConsulting.BLL;
Now I need to create an object from my class to be used throughout my .aspx page. This is done just above the Page_Load event that should already be part of your page by default:
ErrorscError = new Errors();
The last step is to use the cError object in my try-catch block:
try
{
//some code
}
catch (Exception ex)
{
//some code to handle the error
//then log the error
cError.LogError(ex, Page.ToString(), “grdNews_SelectedIndexChanged”, Convert.ToString(Session["Username"]));
}
Let’s look at this final line item by item. First we are refering to the cError object and using its LogError method. Then we are passing in all the needed parameters. First is the Exception “ex”, so we know what went wrong.
Next is the Page title. In this case, the page is my “news.aspx” page, which is what will be written to the log. This let’s me know where the error occured. By using the Page object we don’t have to keep changing this parameter for every method call.
Since the page isn’t enough, I also want to know what method caused the error, so I’ve copied and pasted the event handler title. In this case, that’s grdNews_SelectedIndexChanged. This will need to be changed for each method.
Finally, I want to know which user experienced the error in case I need to follow up with them and learn exactly what they were trying to do when it ocurred. Here I am copying their name out of a session variable.
Conclusion
With code reuse being one of the big advantages of object oriented programming, having all your error logging handled by an identical routine is smart, consistent, and saves time while also giving you flexibability in how you process the logs. This class can be altered to provide other information as you see fit, but it should give you a good start on logging your errors with only one line of code per method. Happy coding!