Microsoft Developer Network Bangladesh
Next Generation Online Developer Community

Creating a custom Performance Counter

A nice feature of Asp.net is to monitor the performance of a web application using PerformanceCounter. This class is under System.Diagnostics namespace and provides helpful features for implementing counters that can be useful for monitoring site activity.

We can observe the counter status from the Performance tool in Administrative Tools of the operating system.

 So, let us create a custom performance counter.

Firstly, we will create an abastract class which will be the base class for all our performance counters for the current web application. 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Web;

namespace MyApp.BLL.Performance
{
    public abstract class Counter
    {
        private const string _categoryName = "MyApp Counters";

        protected abstract string CounterName
        {
            get;
        }

        public int Count
        {
            get
            {
                PerformanceCounterPermission permission = new PerformanceCounterPermission(PerformanceCounterPermissionAccess.Administer, Environment.MachineName, _categoryName);
                permission.Assert();
                if (PerformanceCounterCategory.Exists(_categoryName))
                {
                    PerformanceCounter counter = new PerformanceCounter(_categoryName, CounterName);
                    int count = Convert.ToInt32(counter.RawValue);
                    counter.Close();
                    PerformanceCounterPermission.RevertAll();
                    return count;
                }
                else
                    return 0;
            }
        }

        public int Increase()
        {
            PerformanceCounterPermission permission = new PerformanceCounterPermission(PerformanceCounterPermissionAccess.Administer,
                Environment.MachineName, _categoryName);
            permission.Assert();
            if (PerformanceCounterCategory.Exists(_categoryName))
            {
                PerformanceCounter counter = new PerformanceCounter(_categoryName, CounterName, false);
                long currentCount = counter.Increment();
                PerformanceCounterPermission.RevertAll();
                return Convert.ToInt32(currentCount);
            }
            else
            {
                PerformanceCounterCategory.Create(_categoryName, _categoryName,
                    PerformanceCounterCategoryType.SingleInstance, CounterName, CounterName);
                PerformanceCounter counter = new PerformanceCounter(_categoryName, CounterName, false);
                counter.RawValue = 1;
                PerformanceCounterPermission.RevertAll();
                return Convert.ToInt32(counter.RawValue);
            }
        }

        public int Decrease()
        {
            PerformanceCounterPermission permission = new PerformanceCounterPermission(PerformanceCounterPermissionAccess.Administer,
                Environment.MachineName, _categoryName);
            permission.Assert();
            if (PerformanceCounterCategory.Exists(_categoryName))
            {
                PerformanceCounter counter = new PerformanceCounter(_categoryName, CounterName, false);
                long currentCount = 0;
                if (counter.RawValue > 0)
                {
                    currentCount = counter.Decrement();
                }
                PerformanceCounterPermission.RevertAll();
                return Convert.ToInt32(currentCount);
            }
            else
            {
                PerformanceCounterCategory.Create(_categoryName, _categoryName, PerformanceCounterCategoryType.SingleInstance,
                    CounterName, CounterName);
                PerformanceCounter counter = new PerformanceCounter(_categoryName, CounterName, false);
                counter.RawValue = 0;
                PerformanceCounterPermission.RevertAll();
                return Convert.ToInt32(counter.RawValue);
            }
        }
    }
}

 We will override the CounterName property in every child class, for example:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace MyApp.BLL.Performance
{
    public sealed class UserLoginCounter : Counter
    {
        protected override string CounterName
        {
            get
            {
                return "Logged User Count";
            }
        }
    }
}

 

now we can use the counter in an aspx page like the following:

using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using Linkmate.BLL.Performance;

public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            UserLoginCounter counter = new UserLoginCounter();
            tbxCount.Text = counter.Count.ToString();
        }
    }

    protected void UserEnter_Click(object sender, EventArgs e)
    {
        UserLoginCounter counter = new UserLoginCounter();
        tbxCount.Text = counter.Increase().ToString();

    }

    protected void UserExit_Click(object sender, EventArgs e)
    {
        UserLoginCounter counter = new UserLoginCounter();
        tbxCount.Text = counter.Decrease().ToString();
    }
}

 One important point to note about the PerformanceCounter is, though we have assigned  PerformanceCounterPermission, still we will not be able to access the registry unless the user group accessing the application has right permission to do so. It will raise an exception:

System.UnauthorizedAccessException: Access to the registry key 'Global' is denied.

A solution to this problem has been given here

a snapshot of that article is (in case the page is not available later on):

Windows 2003, Windows XP x64 Edition, and Vista require that the user be part of the Performance Monitor Users group to read performance counter data. Simply adding your non admin user to this group will fix this problem.

Accessing counters remotely is another story though. On Windows 2003, Windows XP x64 Edition, and Vista you still must be part of the Performance Monitor Users group on the remote machine but there is a problem with the PerformanceCounter class where it tries to read some registry keys on the remote machine that a non admin users do not have access to. To give your user read access to these keys without having to be an admin on the remote machine complete the following steps on the remote machine:

  1. Open the Registry Editor by going to the Start Menu and selecting Run…, then type “regedit”, and click the OK button.
  2. Navigate to the HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurePipeServers\winreg registry key.
  3. Right click on the "winreg" key and select Permissions. Add users or groups to which you want to grant Read access.
  4. Exit Registry Editor and restart Windows.

For more explanation on this process see http://support.microsoft.com/?kbid=153183.

 


Posted 03-16-2009 10:25 AM by Jalal

Comments

Mohammad Ashraful Alam wrote re: Creating a custom Performance Counter
on 03-16-2009 6:45 PM

nice post jalal. Keep going!

Powered by Community Server (Non-Commercial Edition), by Telligent Systems