Miranda's Musings

computer articles, politics, and more

Error Notification system

As i mentioned in my previous post, I have a solution for sending me an email whenever an error occurs in the model or on the controller.   This way if something is omitted or invalid data is entered I am notified.  To get this to work I created the following code

The Model will have one method with an overload.  I use the overload to pass the parameters in the sprocs so that way I know if it is a SQL server error what caused it.

using System;
using System.Diagnostics;
using System.Net.Mail;
using System.Web;

namespace MyLibrary.Models
{
    public class Errors
    {
        public static void ErrorOccured(Exception ex)
        {
            var st = new StackTrace(ex, true);
            // Get the top stack frame
            var frame = st.GetFrame(0);
            // Get the line number from the stack frame
            var line = frame.GetFileLineNumber();
            string page = frame.GetFileName();
            var col = frame.GetFileColumnNumber();

            string domain = HttpContext.Current.Request.Url.Host;
            string MailBody = ex.Message.ToString() + "\r\n<br />File: " + page + "\r\n<br />Line number: " + line.ToString() + " column " + col.ToString() + "\r\n<br />";
            string MessageToSend = ex.StackTrace.ToString();
            string MailSender = "the email address used to send the notification";
            string MailRecipient = "The email address you want to send the notification to";
            string MailSubject = "Error on " + domain;
            MailMessage msg = new MailMessage();

            msg.ReplyToList.Add(MailSender);
            msg.To.Add(MailRecipient);
            msg.Subject = MailSubject;
            msg.Body = MailBody + "\r\n<br /><br />" + MessageToSend;
            msg.BodyEncoding = System.Text.UTF8Encoding.UTF8;
            msg.SubjectEncoding = System.Text.Encoding.Default;
            msg.IsBodyHtml = true;
            using (var smtp = new SmtpClient())
            {
                smtp.Send(msg);
            }
        }

        public static void ErrorOccured(Exception ex, string parameters)
        {
            var st = new StackTrace(ex, true);
            // Get the top stack frame
            var frame = st.GetFrame(0);
            // Get the line number from the stack frame
            var line = frame.GetFileLineNumber();
            string page = frame.GetFileName();
            var col = frame.GetFileColumnNumber();

            string domain = HttpContext.Current.Request.Url.Host;
            string MailBody = ex.Message.ToString() + "\r\n<br />File: " + page + "\r\n<br />Line number: " + line.ToString() + " column " + col.ToString() + "\r\n<br />" + parameters + "\r\n<br />";

            string MessageToSend = ex.StackTrace.ToString();
            string MailSender = "the email address used to send the notification";
            string MailRecipient = "The email address you want to send the notification to";
            string MailSubject = "Error on " + domain;
            MailMessage msg = new MailMessage();

            msg.ReplyToList.Add(MailSender);
            msg.To.Add(MailRecipient);
            msg.Subject = MailSubject;
            msg.Body = MailBody + "\r\n<br /><br />" + MessageToSend;
            msg.BodyEncoding = System.Text.UTF8Encoding.UTF8;
            msg.SubjectEncoding = System.Text.Encoding.Default;
            msg.IsBodyHtml = true;
            using (var smtp = new SmtpClient())
            {
                smtp.Send(msg);
            }
        }
    }
}

The above assumes that you have the mailSettings set up in your Web.config file as in below.

<system.net>
	<mailSettings>
		<smtp from="your email address here">
			<network host="your smtp mail host" 
				port="the port you will use  generally 25 or 587" 
				userName="the username typically the email address" 
				password="the password for that email address." 
				enableSsl="true or false, depending on if you use ssl for email" />
		</smtp>
    </mailSettings>
</system.net>

 

Next is the ErrorController 

using System;
using System.Web.Mvc;

namespace MyLibrary.Controllers
{
    public class ErrorController : Controller
    {
         
        public ActionResult Index()
        {
            ViewBag.ErrorType = TempData["ErrorType"];
            ViewBag.Error = TempData["Error"];
            return View();
        }

        public ActionResult BadRequest()
        {
            return View();
        }

        public ActionResult NotAllowed()
        {
            return View();
        }

        public ActionResult PageNotFound(string message, string aspxerrorpath)
        {
            ViewBag.Errorpath = aspxerrorpath;
            ViewBag.Message = message;
            return View();
        }
    }
}

 

Then there is a View called Index which is a custom error page.  It can have anything you want on there.  I give a message telling the user that the programmers have been notified of the error, and also include a graphic of some sort.

Now to use this all you need to do is include a try/catch block of code and in the catch pass the information to the ErrorCcontroller.  On the controller that is done like so

public ActionResult Index(Games games)   
{        
           try
           {
               //some code here
               return View();
            }
            catch (Exception ex)
            {
                Errors.ErrorOccured(ex);
            }
            return RedirectToAction("Index", "Error", null);
}

And in a model the code looks something like this.  As you can see I pass the value of each parameter along with the name of that parameter

public string Add(string GameId, string GameName, string RecommendedAges, string Synopsis, string Link, int MinPlayers, int MaxPlayers, string Playtime, int MinPlaytime)
{
	using (SqlConnection oConn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
	{
		using (SqlCommand oCmd = new SqlCommand())
		{
			try
			{
				oCmd.CommandText = "AddGame";
				oCmd.CommandType = CommandType.StoredProcedure;
				oCmd.Parameters.Add("@GameId", SqlDbType.NVarChar).Value = GameId;
				oCmd.Parameters.Add("@GameName", SqlDbType.NVarChar).Value = GameName;
				oCmd.Parameters.Add("@RecommendedAges", SqlDbType.VarChar).Value = RecommendedAges;
				oCmd.Parameters.Add("@Synopsis", SqlDbType.NVarChar).Value = Synopsis;
				oCmd.Parameters.Add("@Link", SqlDbType.VarChar).Value = Link;
				oCmd.Parameters.Add("@MinPlayers", SqlDbType.Int).Value = MinPlayers;
				oCmd.Parameters.Add("@MaxPlayers", SqlDbType.Int).Value = MaxPlayers;
				oCmd.Parameters.Add("@Playtime", SqlDbType.NVarChar).Value = Playtime;
				oCmd.Parameters.Add("@MinPlaytime", SqlDbType.Int).Value = MinPlaytime;
				oCmd.Connection = oConn;
				oConn.Open();
				string gid = oCmd.ExecuteScalar().ToString();
				return gid;
			}
			catch (Exception ex)
			{
				Errors.ErrorOccured(ex, "GameId = " + GameId + " GameName = " + GameName + " RecommendedAges = " + RecommendedAges +
					" Synopsis = " + Synopsis + " Link = " + Link + " MinPlayers = " + MinPlayers + " MaxPlayers = " + MaxPlayers +
					" Playtime = " + Playtime + " MinPlaytime = " + MinPlaytime);
				return null;
			}
			finally
			{
				oCmd.Dispose();
				oConn.Close();
			}
		}
	}
}
 

This may not be the most elegant solution, but it works for me.  I am notified of all errors that occur in the Controller or in the Model.   The one caveat is if it is an error that occurs on the View, I am not notified.   I had a method that also sent me an email for errors from the view but that used the Global.asax file and would also send me 404 errors.   The reason I don't like that is a lot of bots hit our sites and some of those are looking for exploits and they can hit a page many times in the course of a second before moving on and I am then sent an email for each attempt.  

 



 

 

 

Loading