July 2011

Volume 26 Number 07

MVC Filters - Easily Add Performance Counters to Your MVC Application

By Ben Grover | July 2011

Working on enterprise Web applications usually involves a host of additional code to help with monitoring and operation of the apps. In this article, I’ll explain how I’m using Model-View-Controller (MVC) filters to clean up and replace repeated, confusing code that was spread throughout numerous methods in an application.

Operational managers often set up Microsoft Operations Manager (MOM) to monitor the health of a Web site (or service) and use performance counters to trigger alarms based on threshold values. These alarms help ensure that degraded experiences on the Web site are found quickly.

Problem Code

I’m working on a related project (using the ASP.NET MVC Framework) where the requirements are to add performance counters to Web pages and Web services to aid the operations team. The operations team requires counters for each page: Request Latency, Total Requests per Second and Failure Ratio.

Problems always seem to arise with the implementation of such requirements. I started looking at the current implementation of these counters from more diligent developers who had added them as part of previous coding milestones. I was disappointed. I’m sure you’ve all been there—you look at the code and cringe. What did I see? Repeated code sprinkled through eachmethod, with just a few changes to a variable name here and there. I wasn’t happy with the current implementation.

The code that made me cringe can be seen in Figure 1.

Figure 1 Code that Made Me Cringe

public ActionResult AccountProfileInformation()
{
  try
  {
    totalRequestsAccountInfoCounter.Increment();
    // Start counter for latency
    long startTime = Stopwatch.GetTimestamp();
 
    // Perform some operations action here
    long stopTime = Stopwatch.GetTimestamp();
    latencyAccountInfoCounter.IncrementBy((stopTime - startTime) / 
      Stopwatch.Frequency);
    latencyAccountInfoBaseCounter.Increment();
  }
  catch (Exception e)
  {
    failureAccountInfoCounterCounter.Increment();
  }
  return View();
}

Looking at this code, I knew I wanted to remove it from each of my project’s action methods. This type of pattern makes it extremely hard to see where the true method code is because of all the superfluous code to account for performance monitoring. I was looking for a clever way to refactor this code so that it wouldn’t litter each of the action methods. Enter MVC filters.

MVC Filters

MVC filters are custom attributes that you put onto action methods (or controllers) to add common functionality. MVC filters allow you to add pre- and post-processing behaviors. The list of built-in MVC filters can be found here: bit.ly/jSaD5N. I had used some of the built-in filters, such as OutputCache, but I knew that MVC filters had a lot of power hidden that I’d never tapped into (to read more about the filter attribute, see bit.ly/kMPBYB).

So, I began thinking to myself: What if I could encapsulate all of the logic for these performance counters in an MVC filter attribute? An idea was born! I could meet the requirements of each performance counter listed earlier with the following actions:

  1. Total Requests per Second
    1. Implement IActionFilter, which has two methods: OnActionExecuting and OnActionExecuted
    2. Increment the counter in the OnActionExecuting
  2. Request Latency
    1. Implement IResultFilter, which has two methods: OnResultExecuting and OnResultExecuted
    2. Start the timer in the OnActionExecuting and record the latency during OnResultExecuted
  3. Failure Ratio
    1. Implement IExceptionFilter, which has the method OnException

The process is illustrated in Figure 2.

MVC Filter-Processing Pipeline

Figure 2 MVC Filter-Processing Pipeline

I’ll discuss the use of each filter as shown in Figure 2.

IActionFilter OnActionExecuting (line 2a) executes before the action method is executed. OnActionExecuted (line 2b) executes after the action method is executed, but before the result is executed.

IResultFilter OnResultExecuting (line 4a) executes before the action result is executed (such as rendering a view). OnResultExecuted (line 4b) executes after the action result is executed.

IExceptionFilter OnException (not shown in Figure 2 for clarity) executes whenever an exception is thrown (and unhandled).

IAuthorizationFilter OnAuthorization (not included in Figure 2, nor used in this article) is called when authorization is required.

Managing the Counters

However, if I used this performance counter filter attribute, I’d have a problem: How would I get the performance counter (for each action) into each of these filters at run time? I didn’t want to have a separate filter attribute class for each action. In that case, I’d have to hardcode the performance counter name in the attribute. That would cause an explosion in the number of class names needed to implement the solution. I reflected back on a technology I used when I had first worked with the Microsoft .NET Framework: reflection (pun intended!). Reflection is heavily leveraged by the MVC framework as well. You can learn more about reflection here: bit.ly/iPHdHz.

My idea was to create two classes:

  1. WebCounterAttribute
    1. Implements the MVC filter interfaces (IExceptionFilter, IActionFilter and IResultFilter)
    2. Increment counters stored in WebCounterManager
  2. WebCounterManager
    1. Implements the reflection code for loading the WebCounterAttributes from each MVC action
    2. Stores a map to facilitate the lookup of the performance counter objects
    3. Provides methods to increment the counters stored in that map

Implementing the Design

Having those classes, I could decorate WebCounterAttribute on each of the action methods on which I wanted performance counters to be implemented, as shown here:

public sealed class WebCounterAttribute : FilterAttribute, IActionFilter, IExceptionFilter, IResultFilter
{
  /// Interface implementations not shown
}

Here’s a sample action method:

[WebCounter("Contoso Site", "AccountProfileInformation")]
public ActionResult AccountProfileInformation()
{
  // Some model loading
  return View();
}

Then I could read in these attributes during the Application_Start method using reflection and create a counter for each of these actions, as shown in Figure 3. (Note that counters are registered with the system in a setup program, but instances of the counters are created in code.)

Figure 3 Reflecting Over Assemblies

/// <summary>
/// This method reflects over the given assembly(ies) in a given path 
/// and creates the base operations required  perf counters
/// </summary>
/// <param name="assemblyPath"></param>
/// <param name="assemblyFilter"></param>
public void Create(string assemblyPath, string assemblyFilter)
{
  counterMap = new Dictionary<string, PerformanceCounter>();
            
  foreach (string assemblyName in Directory.EnumerateFileSystemEntries(
    assemblyPath, assemblyFilter)) 
  {
    Type[] allTypes = Assembly.LoadFrom(assemblyName).GetTypes();
 
    foreach (Type t in allTypes)
    {
      if (typeof(IController).IsAssignableFrom(t))
      {
        MemberInfo[] infos = Type.GetType(t.AssemblyQualifiedName).GetMembers();
 
        foreach (MemberInfo memberInfo in infos)
        {
          foreach (object info in memberInfo.GetCustomAttributes(
            typeof(WebCounterAttribute), true))
          {
            WebCounterAttribute webPerfCounter = info as WebCounterAttribute;
            string category = webPerfCounter.Category;
            string instance = webPerfCounter.Instance;
            // Create total rollup instances, if they don't exist
            foreach (string type in CounterTypeNames)
            {
              if (!counterMap.ContainsKey(KeyBuilder(Total, type)))
              {
                counterMap.Add(KeyBuilder(Total, type), 
                  CreateInstance(category, type, Total));
              }
            }
            // Create performance counters
            foreach (string type in CounterTypeNames)
            {
              counterMap.Add(KeyBuilder(instance, type), 
                CreateInstance(category, type, instance));
            }
          }
        }
      }
    }
  }
}

Notice the important line where the map is populated:

(counterMap.Add(KeyBuilder(instance, type), CreateInstance(category, type, instance));),

It creates a mapping between the particular instance of a WebCounterAttribute on an action, including the counter type, and maps it to the created PerformanceCounter instance. 

I could then write the code that enables me to use this mapping to look up the PerformanceCounter instance (and increment it) for a given instance of the WebCounterAttribute (see Figure 4).

Figure 4 WebCounterManager RecordLatency

/// <summary>
/// Record the latency for a given instance name
/// </summary>
/// <param name="instance"></param>
/// <param name="latency"></param>
public void RecordLatency(string instance, long latency)
{
  if (counterMap.ContainsKey(KeyBuilder(instance,   
    CounterTypeNames[(int)CounterTypes.AverageLatency]))
    && counterMap.ContainsKey(KeyBuilder(instance,   
    CounterTypeNames[(int)CounterTypes.AverageLatencyBase])))
  {
    counterMap[KeyBuilder(instance, 
      CounterTypeNames[(int)CounterTypes.AverageLatency])].IncrementBy(latency);
    counterMap[KeyBuilder(Total, 
      CounterTypeNames[(int)CounterTypes.AverageLatency])].IncrementBy(latency);
    counterMap[KeyBuilder(instance, 
      CounterTypeNames[(int)CounterTypes.AverageLatencyBase])].Increment();
    counterMap[KeyBuilder(Total, 
      CounterTypeNames[(int)CounterTypes.AverageLatencyBase])].Increment();
  }
}

Then I could record the performance counter data when these filters are run. For example, in Figure 5, you see an implementation of recording performance latency.

Figure 5 The WebCounterAttribute Invoking the WebCounterManager RecordLatency

/// <summary>
/// This method occurs when the result has been executed (this is just 
/// before the response is returned). 
/// This method records the latency from request begin to response return.
/// </summary>
/// <param name="filterContext"></param>
public void  OnResultExecuted(ResultExecutedContext filterContext)
{
  // Stop counter for latency
  long time = Stopwatch.GetTimestamp() - startTime;
  WebCounterManager countManager = GetWebCounterManager(filterContext.HttpContext);
  if (countManager != null)
  {
    countManager.RecordLatency(Instance, time);
    ...
  }
}
private WebCounterManager GetWebCounterManager(HttpContextBase context)
{
  WebCounterManager manager =  
    context.Application[WebCounterManager.WebCounterManagerApplicationKey] 
    as WebCounterManager;
  return manager;
}

You’ll note in this call that I’m getting the WebCounterManager from the Application State. In order for this to work, you’ll need to add code to your global.asax.cs:

WebCounterManager webCounterMgr = new WebCounterManager();
webCounterMgr.Create(Server.Map("~/bin"), "*.dll");
Application[WebCounterManager.WebCounterManagerApplicationKey] = webCounterMgr;

Wrapping up, MVC filters provide an elegant solution to heavily repeated code patterns. They’ll help you refactor common code that will make your code cleaner and easier to maintain. Obviously, a balance has to be struck between elegance and ease of implementation. In my case, I had to add performance counters to about 50 Web pages. The savings in terms of code readability and clarity was definitely worth the additional effort.

MVC filters are a great way to add behaviors without being obtrusive, so whether you’re dealing with performance counters, logging or auditing, you’ll find limitless possibilities for clean implementation of necessary logic.


Ben Grover is a programmer at Microsoft in Redmond, Wash., where he has worked on multiple teams, from Exchange to Lync to Windows.

Thanks to the following technical expert for reviewing this article: Eilon Lipton