All coders are quite familiar with error messages. They are quite handy when developing stuff but you dont want them around after the release of your system and the users dont really think they are too funny.
So, how can we get rid of these ugly messages and still get nice information when your functions break down? I have written some code (with some inspiration from other error logging examples) that provide your system with some nice features:
- On error, it redirects the user to a page that in English explains that an error has occurred.
- You can set the object to either send an email or write in the EventLog, or both.
- All the browser information is included in the email for support help.
- It can also present an Alert message to the user.
Code explanation:
// The constructor is overloaded and gets the important variables from either the object creation or web.config
public KillahErrHandler()
{
this.MailServer = ConfigurationSettings.AppSettings["MailServer"];
this.EmailReciever = ConfigurationSettings.AppSettings["MailReciever"];
this.LogName = ConfigurationSettings.AppSettings["LogName"];
}
web.config
<appSettings>
<add key="MailServer" value="mail.yourserver.com" />
<add key="MailReciever" value="[email protected]" />
<add key="LogName" value="MyWeb Log Alert" />
</appSettings>
or..
public KillahErrHandler(string _mailserver, string _mailreciever, string _logname)
{
this.MailServer = _mailserver;
this.EmailReciever = _mailreciever;
this.LogName = _logname;
}
The RaiseError Method executes the proper action depending on the MessageType
public void RaiseError(string _message)
{
this.LastMessage = _message;
switch(this.MT)
{
case MessageType.EventLog:
SaveToEventLog(_message);
break;
case MessageType.SendMail:
SendMail(_message);
break;
case MessageType.MailAndEventLog:
SaveToEventLog(_message);
SendMail(_message);
break;
default:
break;
}
}
Saving to Event Log
This option raises a security issue on the server. By default the ASPNET account cannot write to the event log (This is why I have a try, catch expression here).
To solve this:
Open regedt32 (not the standard, regedit) and navigate to:
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\EventLog and click on "Security-->Permissions..." in the menu.
Add the ASPNET account for the local machine and place a check next to "Full Control".
Remember to include all child objects.
private void SaveToEventLog(string _message)
{
try
{
//Create our own log if it not already exists
if(!EventLog.SourceExists(this.EventLogName))
EventLog.CreateEventSource(this.EventLogName,this.EventLogName);
EventLog MyLog = new EventLog();
MyLog.Source = this.EventLogName;
MyLog.WriteEntry(_message, EventLogEntryType.Warning);
}
catch (Exception e)
{
//send us a email and tell that there is a permission problem
SendMail(e.Message);
}
}
Send Email:
Its not much to say about this. It is quite straight forward.
public void SendMail(string _message)
{
MailMessage mail = new MailMessage();
mail.From = this.EmailReciever;
mail.To = this.EmailReciever;
mail.Subject = this.MailSubject;
mail.Body = "Error message:\n"+_message+"\n\n"+GetUserData();
mail.BodyFormat = MailFormat.Text;
SmtpMail.SmtpServer = this.MailServer;
SmtpMail.Send(mail);
}
User details:
When finding out why the user has problems with your web site, it is quite useful to now the configuration.
private string GetUserData()
{
System.Web.HttpBrowserCapabilities browser=HttpContext.Current.Request.Browser;
string s = "Browser Capabilities\n"
+ "IP Address = " + HttpContext.Current.Request.ServerVariables
"REMOTE_ADDR"].ToString()+"\n"
+ "Type = " + browser.Type + "\n"
+ "Name = " + browser.Browser + "\n"
+ "Version = " + browser.Version + "\n"
+ "Major Version = " + browser.MajorVersion + "\n"
+ "Minor Version = " + browser.MinorVersion + "\n"
+ "Platform = " + browser.Platform + "\n"
+ "Is Beta = " + browser.Beta + "\n"
+ "Is Crawler = " + browser.Crawler + "\n"
+ "Is AOL = " + browser.AOL + "\n"
+ "Is Win16 = " + browser.Win16 + "\n"
+ "Is Win32 = " + browser.Win32 + "\n"
+ "Supports Frames = " + browser.Frames + "\n"
+ "Supports Tables = " + browser.Tables + "\n"
+ "Supports Cookies = " + browser.Cookies + "\n"
+ "Supports VBScript = " + browser.VBScript + "\n"
+ "Supports JavaScript = " + browser.JavaScript + "\n"
+ "Supports Java Applets = " + browser.JavaApplets + "\n"
+ "Supports BackgroundSounds = " + browser.BackgroundSounds + "\n"
+ "Supports ActiveX Controls = " + browser.ActiveXControls + "\n"
+ "Browser = "+ browser.Browser+ "\n"
+ "CDF = "+ browser.CDF+ "\n"
+ "CLR Version = "+ browser.ClrVersion+ "\n"
+ "ECMA Script version = "+ browser.EcmaScriptVersion+ "\n"
+ "MSDOM version = "+ browser.MSDomVersion+ "\n"
+ "Supports tables = "+ browser.Tables+ "\n"
+ "W3C DOM version = "+ browser.W3CDomVersion+ "\n";
return s;
}
Examples
This is an example how we try to contact a database and handles the occurring errors:
private void Page_Load(object sender, System.EventArgs e)
{
SqlDataReader objDataReader = null;
SqlConnection myConnection = null;
try
{
string sql = "server=192.168.0.1;uid=username;pwd=mypass;database=mydb";
myConnection = new SqlConnection(sql);
SqlCommand myLogin = new SqlCommand("MyStoredProcedure", myConnection);
myLogin.CommandType = CommandType.StoredProcedure;
SqlParameter myParm1 = myLogin.Parameters.Add("@Id", SqlDbType.Int);
myParm1.Value = 1;
myConnection.Open();
objDataReader = myLogin.ExecuteReader(CommandBehavior.CloseConnection);
}
catch (SqlException sqlex) // Catch SQL error message
{
//Create object
KillahErrHandler KEH = new KillahErrHandler();
//Set MessageType
KEH.MsgType = MessageType.MailAndEventLog;
//Raise the error
KEH.RaiseError(sqlex.Message);
//Send an alert to the user
Response.Write(KEH.ShowAlert("An database error has occured!",true,true));
}
catch (Exception ex) // Catch other exception message
{
//Create object
KillahErrHandler KEH = new KillahErrHandler();
//Set MessageType
KEH.MsgType = MessageType.MailAndEventLog;
//Raise the error
KEH.RaiseError(ex.Message);
//Send an alert to the user
Response.Write(KEH.ShowAlert("An database error has occured!",true,true));
}
finally //Close DB objects
{
if(objDataReader != null) objDataReader.Close();
if(myConnection != null) myConnection.Close();
}
}