re·cur·sive

/re'kersiv/
aaron@recursv.com:~$ cd aaron/Entry
aaron@recursv.com:~$ cat CreatingDynamicImagesOfServerMetricsData.txt

Creating Dynamic Images Of Server Metrics Data

Wed 04/29/2020

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).  smiley

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://aaronjlynch.com/metrics


Home
|