In a previous post, I mentioned creating some simple way for me to monitor the health of my little mini server. The info I wanted to monitor was How hard is the CPU working?, Is there any sort of memory leak in my code?, and How stable was the machine? In order to create a quick view of this info, but also make it easily reused as blog content, I decided to create images of each metric.
First thing you'll need to do is grant permissions to your AppPool user. If you have created an AppPool for your site, change "DefaultAppPool" to match your chosen name.
net localgroup "Performance Monitor Users" "IIS AppPool\DefaultAppPool" /add
Once my application had the necessary permissions to read this data, I created a new controller "MetricsController" as seen below:
public class MetricsController : Controller
{
// GET: Metrics
public ActionResult Index()
{
return View();
}
[OutputCache(CacheProfile ="ImageCache")]
public ActionResult CPU()
{
return GetCounterImage(new PerformanceCounter("Processor", "% Processor Time", "_Total"), "%");
}
[OutputCache(CacheProfile = "ImageCache")]
public ActionResult FreeMem()
{
return GetCounterImage(new PerformanceCounter("Memory", "Available MBytes"), " MB Free", delegate (double data) { return String.Format("{0:n0}", data); });
}
[OutputCache(CacheProfile = "ImageCache")]
public ActionResult Uptime()
{
return GetCounterImage(new PerformanceCounter("System", "System Up Time"), "", delegate (double data) { return TimeSpan.FromSeconds(data).ToString(); });
}
private FileContentResult GetCounterImage(PerformanceCounter counter, string label,Func<double,string> formatter=null)
{
var measurement = counter.NextValue();
Thread.Sleep(500); //have to read the counter twice to get a good value
measurement = counter.NextValue();
//apply formatter and label to measurement text
string imageText = (formatter == null ? measurement.ToString(): formatter.Invoke(measurement)) + label;
var image = DrawTextImage(imageText, new Font("Arial", 16), Color.Green, Color.Black,Size.Empty);
using (var ms = new MemoryStream())
{
image.Save(ms, ImageFormat.Png);
return File(ms.ToArray(), "image/png");
}
}
private Image DrawTextImage(String currencyCode, Font font, Color textColor, Color backColor, Size minSize)
{
//first, create a dummy bitmap just to get a graphics object
SizeF textSize;
using (Image img = new Bitmap(1, 1))
{
using (Graphics drawing = Graphics.FromImage(img))
{
//measure the string to see how big the image needs to be
textSize = drawing.MeasureString(currencyCode, font);
if (!minSize.IsEmpty)
{
textSize.Width = textSize.Width > minSize.Width ? textSize.Width : minSize.Width;
textSize.Height = textSize.Height > minSize.Height ? textSize.Height : minSize.Height;
}
}
}
//create a new image of the right size
Image retImg = new Bitmap((int)textSize.Width, (int)textSize.Height);
using (var drawing = Graphics.FromImage(retImg))
{
//paint the background
drawing.Clear(backColor);
//create a brush for the text
using (Brush textBrush = new SolidBrush(textColor))
{
drawing.DrawString(currencyCode, font, textBrush, 0, 0);
drawing.Save();
}
}
return retImg;
}
}
And a simple Index.cshtml view to display the new images:
<h2>Server Performance</h2>
<b>CPU Utilization</b>
<br />
<img src="/Metrics/CPU" />
<br />
<b>Available Memory</b>
<br />
<img src="/Metrics/FreeMem" />
<br />
<b>Uptime</b>
<br />
<img src="/Metrics/Uptime" />
The main reason I wanted to serve these up as images was to make it easier to include the values in my blog post(s).
CPU Utilization
Available Memory
Uptime
Now I can take a high-level look at how things are going anytime I want by heading to http://recursv.com/metrics