At work we run Windows Servers and IIS. Occasionally, one of those app pools will misbehave and shut down without anyone knowing until the client calls in and says their site is down with a 503 error. Embarrassing.
We needed a method of monitoring these and letting us know. In researching the solution, I found that we could even restart them.
Start with an empty Windows Service project and add the installer
Next, add your BackgroundWorker
and Timer
objects.
const string connString = "Data Source=localhost;Initial Catalog=dbname;User ID=dbuser;Password=dbpass;Integrated Security=false;Max Pool Size=200";
BackgroundWorker worker;
Timer timer = new Timer(300_000);
In C# 7, you can use the underscore as a thousands separator. So in this case, I've set it to every 5 minutes. The connString
is only needed if you want to use a database for keeping track of app pool starts and make use of notifications. If you plan on notifying every time, then the connection string isn't really needed, either.
Next modify the OnStart
method to look like this and create the callback for the BackgroundWorker
:
protected override void OnStart(string[] args)
{
worker = new BackgroundWorker();
worker.DoWork += Worker_DoWork;
timer.Elapsed += Timer_Elapsed;
timer.Start();
}
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
worker.RunWorkerAsync();
}
private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
}
For the next part, add a reference to Microsoft.Web.Administration
. The path will be %systemroot%\System32\inetsrv\.
Once that's installed, add this to the body of your Worker_DoWork
method (you'll need to add the using Microsoft.Web.Administration;
statement):
try
{
ServerManager manager = new ServerManager();
ApplicationPoolCollection applicationPoolCollection = manager.ApplicationPools;
//List<string> pools = new List<string>(); // uncomment to keep track of app pools that you need to be notified about
foreach (ApplicationPool applicationPool in applicationPoolCollection)
{
if (applicationPool.State == ObjectState.Stopped && !applicationPool.Name.ToLower().Contains("trunk"))
{
ObjectState state = applicationPool.Start();
//AddStarted(applicationPool.Name); // record in whatever medium that the app pool has been started
//if (NeedsNotified(applicationPool.Name)) // determine if service need to notify.
// pools.Add(applicationPool.Name);
}
}
// send a notification, using list of pools as the body of the message
}
catch (Exception ex)
{
// record error to Windows Event Log
EventLog.WriteEntry("AppPoolMonitor", $"{ex.Message}\n\n{ex.StackTrace}", EventLogEntryType.Error);
}
Build, install, and run!
One other thing to note - a number of the solutions I found included using ServerManager.OpenRemote(machineName);
and that didn't work for me. Using the local machine's name it would return null
and throw an exception. removing that line fixed it, as I was running the service on the same server as IIS.
There's one "gotcha" to be aware of.
I noticed that in testing it, IIS wouldn't register that the app pool was running again when I stopped it manually and the service restarted it for me (the site was back online, IIS just thought it was still stopped). This hasn't been an issue in production - the app pools appear running while running when IIS turns them off automatically.
Our system will notify us if the app pool has been started 5 or more times in the last 30 days. Your mileage may vary.
The table structure I used for the tracking:
GETDATE()
)See a full example here: https://github.com/ahwm/IISAppPoolMonitor