Web Site Monitor Application

Doing work as a developer and system administrator has some advantages.  One being that if I don’t like some of the tools that are available to system administrators, I can write my own.  We have all kinds of monitoring tools in place at my company that email me everything from drive space issues, event logs errors, ping responses and just about everything else except if the application is actually up and displaying expected information.  This means I get 200+ emails a day from systems, fine on workdays, but not so good on weekends. 
The solution I came up with was to write a custom console application that reads in the HTML counts the charters.  Based on the number of characters or if an error is returned the code sends an email and a text message to specified accounts.  The reason I count the characters is because an application can throw up an error page like the IIS default page or ‘cannot connect to content database’ SharePoint error without throwing an exception.  If needed, the code could easily be modified to test each application within a known range that the character count should be in; however I found this to be unneeded.   
The reason that I am using a gmail account to send non-corporate emails is because of the settings in our internal exchange server.  I could not email to an external account without changing our exchange server settings and I don’t have control of that in our environment.  I email the SMS or text accounts because during nights and weekends I cannot manage the 200+ emails I am getting, however I do look at text messages.
A few notes:
I debated using a windows service to do this (and even wrote the code), however after some research I found that when I am only polling applications once every thirty minutes it is better to use a console application and a scheduled task.  It is less overhead in code, and I read that the service timers can be somewhat problematic when they go on for that long of a time period.  If someone had a need to set this up to run every minute or several times a minute I would go with a service.
I implemented the ‘WarmUpAppPool’ method because I am checking several SharePoint sites.  Because the application pool goes to sleep after twenty minutes unless otherwise configured I was getting false alerts because the request was timing out.  The app pool warm up basically does a dry run of the actual code and has reduced the false positive issue.  It has the added benefit that the app pool gets warmed up so that the first user in the morning does not get that classic 20 – 30 second first page load time.
Program.cs file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Net;
using System.Net.Mail;
using System.Configuration;

namespace WebAppMonitorCA
{
    class Program
    {
        static void Main(string[] args)
        {
            //The WarmUpAppPool method does a dry run on this to wake any sleeping app pools and prevent false alerts.
            WarmUpAppPool();
           
            //String of Emails with a ; separator EX: YourName@yourCo.com;ThereName@yourCo.com;3035551234@txt.att.net
            string[] NotifcationEmails = ConfigurationSettings.AppSettings["Emails"].Split(';');
           
            //String of Apps URLs with a ; separator EX: http://portal/;http://corpdir/;http://mysite/
            string[] AppURLs = ConfigurationSettings.AppSettings["AppURLs"].Split(';');
           
            //Path the the Should be in EX: C:\Logs\
            string LogPath = ConfigurationSettings.AppSettings["LogPath"];

            //Min count of chars that should be on a page.  IIS 7 default page is under 1000
            //Sends an error if the page is up but there is no content
            int LowCount = System.Convert.ToInt32(ConfigurationSettings.AppSettings["LowCount"]);

            //Create unique log name
            string LogDate = "_" + DateTime.Now.Date.Month.ToString();
            LogDate += "_" + DateTime.Now.Date.Day.ToString();
            LogDate += "_" + DateTime.Now.Hour.ToString();
            LogDate += "_" + DateTime.Now.Minute.ToString();

            using (StreamWriter sw = new StreamWriter(LogPath + "WebAppMonitorLog" + LogDate + ".txt"))
            //using (StreamWriter sw = new StreamWriter("C:\\WebAppMonitorLog" + LogDate + ".txt"))
            {
                //Cycle each web app and test it
                foreach (string AppURL in AppURLs)
                {
                    try
                    {
                        string strContent = String.Empty;
                        string strSource = String.Empty;
                        StreamReader objSR;
                        WebResponse objResponse = null;
                        WebRequest objRequest = HttpWebRequest.Create(AppURL);
                        objRequest.Credentials = CredentialCache.DefaultCredentials; //Make sure to run the task with an account that has enough rights to access these apps
                        objResponse = objRequest.GetResponse();
                        int x = 0;
                        objSR = new StreamReader(objResponse.GetResponseStream(), System.Text.Encoding.ASCII);
                        strContent = objSR.ReadToEnd();
                        x = strContent.Length; //Get count of chars of page content
                        strSource += "Application: " + AppURL;
                        strSource += "\t DateTime: " + DateTime.Now.ToString();
                        strSource += "\t Content-Length: " + x.ToString();
                        objSR.Close();
                        objResponse.Close();
                        sw.WriteLine(strSource);
                        if (x < LowCount)
                        {
                            EmailAdmins(AppURL, "Page char count Under " + LowCount.ToString() + ". Normally this is an error page");
                        }
                    }
                    catch (Exception ex)
                    {
                        sw.WriteLine(" ERROR in App: " + AppURL + "\t DateTime: " + DateTime.Now.ToString() + "\t Message: " + ex.Message.ToString());
                        EmailAdmins(AppURL, ex.Message.ToString());
                    }
                }
            }    
        }

        //Email a TEXT
        //AT&T – cellnumber@txt.att.net
        //Verizon – cellnumber@vtext.com
        //T-Mobile – cellnumber@tmomail.net
        //Sprint PCS - cellnumber@messaging.sprintpcs.com
        //Virgin Mobile – cellnumber@vmobl.com
        //US Cellular – cellnumber@email.uscc.net
        //Nextel - cellnumber@messaging.nextel.com
        //Boost - cellnumber@myboostmobile.com
        //Alltel – cellnumber@message.alltel.com

        static void EmailAdmins(string Application, string ex)
        {
            string[] NotifcationEmails = ConfigurationSettings.AppSettings["Emails"].Split(';');
            string AllEmails = ConfigurationSettings.AppSettings["Emails"];
            //Tricky Emails 
            foreach (string Email in NotifcationEmails)
            {
                try
                {
                    //If the email does is  not within the company (text mail, hotmail, gmail ext) I created a Gmail account to use
                    //This is only needed if your cannot enable SMTP Relaying on your mail servers. (I can't - not my servers)
                    if (!Email.Contains("yourcompany"))
                    {
                        string AppName = Application.Replace("http://", " ").Replace('/', ' ');
                        MailMessage mail = new MailMessage();
                        SmtpClient SmtpServer = new SmtpClient("smtp.gmail.com");
                        mail.From = new MailAddress("coyourcompany@gmail.com");
                        mail.To.Add(new MailAddress(Email));
                        mail.Subject = String.Format("Application Down");
                        mail.Body = String.Format("The following application is down:  " + AppName + ". Message is - " + ex);
                        SmtpServer.Port = 587;
                        SmtpServer.Credentials = new System.Net.NetworkCredential("coyourcompany@gmail.com", "yourcompanyGmailpw");
                        SmtpServer.EnableSsl = true;
                        SmtpServer.Send(mail);
                    }
                    else  //Internal email, send it the normal way
                    {
                        MailMessage m = new MailMessage();
                        m.To.Add(new MailAddress(Email));
                        m.From = new MailAddress("mosssvc@yourcompany.com");
                        string AppName = Application.Replace("http://", " ").Replace('/', ' ');
                        m.Subject = String.Format("Application Down :" + AppName);
                        m.Body = String.Format("The following application is down:  " + Application);
                        //m.Body += ". Message: " + ex;
                        //m.Body += "\nThis email was sent to the following address:  " + AllEmails;
                        m.Priority = MailPriority.High;
                        SmtpClient client = new SmtpClient("mail.yourcompany.com");
                        // Send the mail message
                        client.Send(m);
                    }
                }
                catch (Exception MailAddressInvalid)
                {
                    //If one email fails we still want to email everyone else
                }
            }
        }

        static void WarmUpAppPool()
        {
            string[] AppURLs = ConfigurationSettings.AppSettings["AppURLs"].Split(';');
            foreach (string AppURL in AppURLs)
            {
                try
                {
                    string strContent = String.Empty;
                    string strSource = String.Empty;
                    StreamReader objSR;
                    WebResponse objResponse = null;
                    WebRequest objRequest = HttpWebRequest.Create(AppURL);
                    objRequest.Credentials = CredentialCache.DefaultCredentials;
                    objResponse = objRequest.GetResponse();
                    int x = 0;
                    objSR = new StreamReader(objResponse.GetResponseStream(), System.Text.Encoding.ASCII);
                    strContent = objSR.ReadToEnd();
                    x = strContent.Length;
                    strSource += " Content-Length: " + x.ToString();
                    objSR.Close();
                    objResponse.Close();
                }
                catch (Exception ex)
                {
                   //This is to warm up the app bool so close catch the ex
                }
            }
        }
    }
}

The appconfig
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <bindings />
        <client />
    </system.serviceModel>
  <appSettings>
    <add key="Emails" value="yourmail@hotmail.com;3235555555@txt.att.net;7205555555@txt.att.net; admin@yourco.com" />
    <add key="AppURLs" value="http://yourAppURL/;http://yourAppURL/;http://yourAppURL" />
    <add key="LogPath" value="D:\ScheduledTasks\WebAppMonitorLogs\" />
    <!--<add key="LogPath" value="C:\" />-->
    <add key="LowCount" value="1000" />
  </appSettings>

</configuration>



Comments

Popular posts from this blog

Metastorm - Open form in an action from a list or link

Corporate Intranet Information Architecture – a Publishing Site

No Search Results in SharePoint Contextual Search OSSSearchResults.aspx