How to create a custom HealthMonitoring provider that sends e-mails?

Category:
ASP.net
Publishdate:
26.02.2011
Hits:
4810

Introduction

This article shows you have you can write your own HealthMonitoring provider. The ASP.net HealthMonitoring is a very powerful tool that allows you to execute actions upon errors, like sending an e-mail, writing an entry into the eventlog or into a database.

I will show you how you send an email with your own HealthMonitoring custom provider.

You may ask yourself why I would write another HealthMonitoring e-mail provider. The reason for this is, that I can't configure the standard SimpleMailWebEventProvider to work with my e-mail provider. As a result, I've built my own provider that creates an errorreport and sends it as System.Net.Mail.MailAddress via System.Net.Mail.SmtpClient

Write your own customprovider for HealthMonitoring

At first you need to create a class that inherits from BufferedWebEventProvider. 
You can implement your own code into the ProcessEventFlush method to get more/less information or use the following class and replace the SendMail method with the action that your own provider should perform.

If you are interested in the depth behind HealthMonitoring, you might check out this MSDN article

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.Management;
using System.Collections.Specialized;
using System.Text;
using System.Threading;
using System.Net.Mail;
using System.Net;

namespace Tomot.HealthMonitoring
{

public class CustomMailProvider : BufferedWebEventProvider
{

	#region Settings
	protected String emailTo = string.Empty;
	protected String emailFrom = string.Empty;
	protected String emailSubject = "Exception in your web application";
	protected bool emailSubjectModified = false;
	protected String emailUserName = string.Empty;
	protected String emailPassword = string.Empty;
	protected String emailHost = string.Empty;
	protected int emailPort = 25;
	#endregion

	protected StringBuilder sb = new StringBuilder();

	public override void Initialize(String name, NameValueCollection config)
	{
		this.emailTo = config["To"];
		config.Remove("To");
		this.emailFrom = config["From"];
		config.Remove("From");
		if (!String.IsNullOrEmpty(config["Subject"]))
		{
			this.emailSubject = config["Subject"];
		}
		config.Remove("Subject");
		this.emailUserName = config["UserName"];
		config.Remove("UserName");
		this.emailPassword = config["Password"];
		config.Remove("Password");
		this.emailHost = config["Host"];
		config.Remove("Host");
		if (!String.IsNullOrEmpty(config["Port"]))
		{
			this.emailPort = int.Parse(config["Port"]);
		}
		config.Remove("Port");
		base.Initialize(name, config);
	}

	public override void ProcessEvent(System.Web.Management.WebBaseEvent eventRaised)
	{
		if (UseBuffering)
		{
			base.ProcessEvent(eventRaised);
		}
		else
		{
			this.SendMail();
		}

	}

	public override void ProcessEventFlush(System.Web.Management.WebEventBufferFlushInfo flushInfo)
	{
		foreach (WebBaseEvent eventRaised in flushInfo.Events)
		{

			WebRequestErrorEvent webRequestErrorEvent = (WebRequestErrorEvent)eventRaised;
			WebRequestInformation wri = webRequestErrorEvent.RequestInformation;
			WebBaseErrorEvent webBaseErrorEvent = (WebBaseErrorEvent)eventRaised;
			Exception exception = webBaseErrorEvent.ErrorException;
			WebProcessInformation webProcessInformation = webBaseErrorEvent.ProcessInformation;
			if (!this.emailSubjectModified)
			{
				this.emailSubject = String.Format("{0}: {1}",
					this.emailSubject,
					exception.GetType().Name);
				this.emailSubjectModified = true;
			}
			sb.AppendFormat("<b>** Applicationinformation **</b>");
			sb.AppendFormat("ApplicationPath: {0}", HttpRuntime.AppDomainAppPath);
			sb.AppendFormat("Virtual ApplicationPath: {0}", HttpRuntime.AppDomainAppVirtualPath);
			sb.AppendFormat("MachineName: {0}", Environment.MachineName);
			sb.AppendFormat("");
			sb.AppendFormat("<b>** Eventinformation **</b>");
			sb.AppendFormat("EventTime: {0}", eventRaised.EventTime);
			sb.AppendFormat("EventTimeUtc: {0}", eventRaised.EventTimeUtc);
			sb.AppendFormat("\n");
			sb.AppendFormat("<b>** Requestinformation **</b>");
			sb.AppendFormat("RequestUrl: {0}", wri.RequestUrl);
			sb.AppendFormat("RequestPath: {0}", wri.RequestPath);
			sb.AppendFormat("UserHostAddress: {0}", wri.UserHostAddress);
			sb.AppendFormat("Name: {0}", wri.Principal.Identity.Name);
			sb.AppendFormat("IsAuthenticated: {0}", wri.Principal.Identity.IsAuthenticated);
			sb.AppendFormat("AuthenticationType: {0}", wri.Principal.Identity.AuthenticationType);
			sb.AppendFormat("ThreadAccountName: {0}", wri.ThreadAccountName);
			sb.AppendFormat("");
			sb.AppendFormat("<b>** Exceptioninformation **</b>");
			sb.AppendFormat("Message: {0}", eventRaised.Message);
			sb.AppendFormat("Exception: {0}", exception.GetType().Name);
			sb.AppendFormat("ExceptionMessage: {0}", exception.Message);
			sb.AppendFormat("StackTrace: {0}", exception.StackTrace.Replace("\n", ""));
			sb.AppendFormat("");
			sb.AppendFormat("<b>** Processinformation **</b>");
			sb.AppendFormat("ProcessID: {0}", webProcessInformation.ProcessID);
			sb.AppendFormat("ProcessName: {0}", webProcessInformation.ProcessName);
			sb.AppendFormat("AccountName: {0}", webProcessInformation.AccountName);
			sb.AppendLine("*******************************************");
		}
		this.SendMail();
	}

	protected void SendMail()
	{
		try
		{
			MailMessage mailMsg = new MailMessage();
			mailMsg.To.Add(this.emailTo);
			mailMsg.IsBodyHtml = true;
			MailAddress mailAddress = new MailAddress(this.emailFrom);
			mailMsg.From = mailAddress;
			mailMsg.Subject = this.emailSubject;
			mailMsg.Body = this.sb.ToString();
			SmtpClient smtpClient = new SmtpClient(this.emailHost, this.emailPort);
			var credentials = new NetworkCredential(this.emailUserName, this.emailPassword);
			smtpClient.Credentials = credentials;
			smtpClient.Send(mailMsg);
		}
		catch
		{
		}

	}

	public override void Shutdown()
	{
		base.Shutdown();
		Flush();
	}
}
}

Configure your customprovider in the web.config

You might have seen in the code above, that my provider does not contain hardcoded settings for my mailprovider (like host, port, username, password). It checks for specified settings that are available through a NameValueCollection. This NameValueCollection retrieves it's value from the HealthMonitoring entry in the web.config

This is how I configure my provider:

<healthMonitoring enabled="false">
	<bufferModes>
		<add name="Log Notification" maxBufferSize="1" maxFlushSize="1" urgentFlushThreshold="1"
		  regularFlushInterval="00:00:20" urgentFlushInterval="00:00:10"/>
	</bufferModes>
	<providers>
		<add name="CustomMailProvider" buffer="true" bufferMode="Log Notification"
		  To="error@yourdomain.de" From="error@yourdomain.de" Subject="HealthMonitoring www.tomot.de"
                  UserName="YourSMTPUserName" Password="YourPassword" Host="smtp.yourprovider.com"
		  Port="25" type="Tomot.HealthMonitoring.CustomMailProvider"/>
	</providers>
	<profiles>
		<add name="Custom" minInstances="1" maxLimit="Infinite" minInterval="00:00:00"/>
	</profiles>
	<rules>
		<add name="CustomMailProvider" eventName="All Errors"
			provider="CustomMailProvider" profile="Custom"/>
	</rules>
</healthMonitoring>

It's worth to mention that you can add as much attributes to the provider as long as you remove them from the NameValueCollection in the Initialize event before base.Initialize gets called. If you don't remove all custom attributes, you will get an exception.

How to use the code posted above

So basically I posted all the code you need to use my custom HealthMonitoring provider.

You can insert the class into your own project or into a seperate assembly. Just make sure that the type attribute of the provider matches the full classname including the assembly.

My namespace is Tomot.HealthMonitoring.CustomMailProvider. You see that the type attribute matches:
type="Tomot.HealthMonitoring.CustomMailProvider"

If you namespace is YourNameSpace.ClassName, the type attribute would look like this:
type="YourNameSpace.ClassName"

Of course you need to insert your settings for your own mailaccount.


Comments:

neha - 05.03.2012 - 21:19
Hi, Can you tell me when I raise an exception, how do I make it hit the process event function in the custom provider ?

Add a comment (due to recent massive spam, I've decided to only display new comments after a manual spam check):
Name
E-Mail (will not be displayed)  
Text