Monday, June 05, 2006

Handling errors in ASP.NET

It would be nice if my sites never crashed, but unfortunately they occasionally do. First off I don't want users to see the ugly ASP.NET error page, so I add this to web.config. <customErrors mode="RemoteOnly" defaultRedirect="error.html"> <error statusCode="404" redirect="index.aspx" /> </customErrors> I redirect to a HTML page, just in case ASP.NET is so ill it can't display an ASPX page (although whether it will be able to redirect at all is open to question). The exception to this is 404 errors which are redirected to the home page. The code below is what I use to handle unexpected errors. This is added into global.asax. Some people will say log errors to the event log but I prefer logging to the database because it's easier to configure. By default, the ASP.NET user can't write to the Application event log. You can set up your own event log, but again this requires some configuration. Most projects have a database back-end, so I just need to add another table. I also send out an email so if there is a serious problem, I get notified pretty quickly. Note, the error also includes the full URL so if a test server or development machine starts sending out error emails, it's clear it's not a real problem. You could also configure this from web.config. Update - Now logs more information about the error, such as the type of browser making the request and any session data. Any objects being stored in session data must have a sensible ToString() implementation
public static void LogError(string errorMessage)
{
// log error to database
using (SqlConnection connection = new SqlConnection())
{
connection.ConnectionString = ConfigurationSettings.AppSettings["connectionString"];
connection.Open();
using (SqlCommand command = connection.CreateCommand())
{
command.CommandText = "INSERT INTO Log VALUES (GETDATE(), @Error)";
command.Parameters.Add("@Error", errorMessage);
command.ExecuteNonQuery();
}
}

// send email
SmtpMail.Send("webserver@initech.com", "doogal@doogal.co.uk", 
"IniTech web server information", errorMessage);
}


protected void Application_Error(Object sender, EventArgs e)
{
// log exceptions
Exception ex = Server.GetLastError().GetBaseException();

StringBuilder errorMessage = new StringBuilder();
errorMessage.AppendFormat("An unexpected error occurred{0}", Environment.NewLine);
errorMessage.AppendFormat("Type: {0}{1}", ex.GetType().ToString(), Environment.NewLine);
errorMessage.AppendFormat("Source: {0}{1}", Request.Url.AbsoluteUri, Environment.NewLine);
errorMessage.AppendFormat("Message: {0}{1}", ex.Message, Environment.NewLine);
errorMessage.AppendFormat("Browser: {0}{1}", Request.UserAgent, Environment.NewLine);
errorMessage.AppendFormat("Stack trace: {0}{1}{2}", Environment.NewLine, ex.StackTrace, Environment.NewLine);
errorMessage.AppendFormat("Session data: {0}", Environment.NewLine);
foreach (string key in Session)
{
  string val = string.Empty;
  if (Session[key] != null)
    val = Session[key].ToString();
    
  errorMessage.AppendFormat("  {0}: {1}{2}", key, val, Environment.NewLine);
}

LogError(errorMessage.ToString());
}

No comments: