AppStats


 
line_chart.png

Inspired by this post by Etsy and from my personal experience with large enterprise systems, application metrics are an important aspect of understanding your system and what it's up to....I love this quote from the Etsy article..."If it moves, we track it. Sometimes we’ll draw a graph of something that isn’t moving yet, just in case it decides to make a run for it"! I also agree with their approach of making it ridiculously easy for any developer to measure something and this is what really motivated me to add this feature to Wolfpack.

The AppStats feature allows you to count or time any operation you think is worth knowing about.

It's easy to add to any .Net application, console, web, WPF

The data is available for easy visualisation/graphing in Geckoboard via the Geckoboard Data Service that ships with Wolfpack so you can instantly stay in touch with trends and spikes as they happen.

geckoboard-intro - small.png
(image taken from geckoboard site - hope you don't mind Paul?!)

How does it work?

AppStats uses existing Wolfpack infrastructure to do the heavy lifting - NServiceBus is used to publish the data from your .Net application to a Wolfpack instance where it is stored in the Wolfpack database and made available to Geckoboard via the Data Service. NServiceBus was selected as it provides a high performance, robust mechanism to transfer the data without impacting local application performance, however AppStats is fully pluggable so if you wanted to use a different transport then it would be simple enough to provide an implementation and plug it in.
So as it stands...
  • NServiceBus transport (using MSMQ)
  • SqlServer, SQLite databases/providers for Geckoboard Data Service (MongoDb Data Service database/provider coming soon).
  • Count anything as a double datatype
  • Time anything and capture the duration in milliseconds
  • Pluggable client design
  • Fluent interface for client component - easy set up and extendible via extension methods

Ok, it measures things...example please!

Right, let's create some real world scenarios to put AppStats to use in.

Scenario 1 - web application, login analytics

You have a login page on your website that a user can log in to and let's suppose you wanted to know more about the behaviour of this page, understanding more about why people fail to login could help you redesign your page to make it easier to use, improve login rate and therefore customer satisfaction; so you would like to track how many users entered an incorrect userid and how many get their password wrong. Now you could make your application capture this information and save it to some data store but it is unlikely that you would have initially designed your application to do this as it has no (apparent) value - the code is there to get you logged in after all. Let's see how easy it is to add AppStats to help solve this. Here is the server side psuedo code for login...

private bool AuthenticateUser(string userid, string password)
{
    // userSvc is the user service and is injected into this component
    var user = userSvc.Load(userid);
    if (user == null)
        return false;
    if (string.Compare(password, user.Password) != 0)
        return false;
    return true;
}

Let's get AppStats in there...
  1. You will need the Wolfpack assemblies to add a reference to. Download Wolfpack and add a reference to the Wolfpack.Core and Wolfpack.Core.Interfaces assemblies.
    1. I am in the process of re-organising the codebase, existing nuget packages to make developing your own plug-ins and including AppStats in your app even easier...I've even got a plan for a self-updating wolfpack with embedded NuGet!
  2. Add in AppStats calls to track the events

private bool AuthenticateUser(string userid, string password)
{
    // userSvc is the user service and is injected into this component
    var user = userSvc.Load(userid);
    if (user == null)
    {
        AppStatsEngine.Publish(new AppStatsEvent().PieChart("LoginFailures").Segment("Unknown User").One());
        return false;
    }
    if (string.Compare(password, user.Password) != 0)
    {
        AppStatsEngine.Publish(new AppStatsEvent().PieChart("LoginFailures").Segment("Incorrect Password").One());
        return false;
    }
    return true;
}

All that remains is to bootstrap AppStats in your application startup - this initialises the sub-system and allows you to connect AppStats to your existing NServiceBus if your application already uses it. Inside your Global.asax Startup() place...

// if you already have a NServiceBus instance
AppStatsEngine.Initialise(AppStatsConfigBuilder.For("AppStatsDemo").PublishWith(myBus).Build());
If you don't use NServiceBus in your application, AppStats can create it for you...
AppStatsEngine.Initialise(AppStatsConfigBuilder.For("AppStatsDemo")
    .PublishWith(BusBuilder.ForApplication()
            // use this if you already have an IoC container 
            // (otherwise the default NSB container is used)
            //.UseContainer(myContainer)
                                                           
            // if you want to customise the msmq settings
            // then use this method to do it otherwise the
            // default queues are used
            //.Msmq("AlternateInput", "AlternateError")

            // that's it - start it!
            .FireItUp(),
        // Optionally specify the destination queue for messages
        // here otherwise AppStats will assume you 
        // have configured this routing in external
        // configuration for the Send() method
        ConfigurationManager.AppSettings["queue"])
    .Build());

Finally you will need a Wolfpack instance running to receive and process the AppStats messages.

Configuring Wolfpack

In order for Wolfpack to be able to process AppStat messages there is some configuration to do. The NServiceBus bridge requires enabling to pick up the messages then one of the database publishers should be selected to persist the data. Finally the Geckoboard Data Service must be enabled to expose the AppStats data.
  1. In activity.castle.config....
    1. GeckoboardDataServiceActivityConfig component "Enabled" property is "true"
    2. GeckoboardDataServiceConfig component "DataProvider" property is set to the right supported database (SqlServer or SQLite)
  2. In publisher.castle.config....
    1. SQLiteConfiguration or SqlServerConfiguration component "Enabled" property is "true"
  3. In startup.castle.config....
    1. BusBridgeConfig component "Enabled" property is "true"

Testing

A demo client is provided in the separate AppStatsDemo binaries zip file. Unzip this and run the "Wolfpack.AppStats.Demo.exe" application - this provides a simple winform app that will send AppStat messages over NServiceBus to Wolfpack.

AppStatDemoClient.png

Geckoboard

To visualise your AppStat data in Geckoboard you will need to add some widgets to your Geckoboard. AppStat data is particularly suited to the Line and Pie chart widgets and these are the urls you will need when you add these widgets. This page is dedicated to setting up Wolfpack with Geckoboard, please refer to this for more information.

To display the Wolfpack Voting example data from the demo app in a piechart use this url with a Geckoboard Piechart widget,
http://yourhost/geckoboard/piechart/WolfpackPoll/any/count

To display the "TestCounterKpi" AppStat from the Count example use this url,
http://yourhost/geckoboard/linechart/TestCounterKpi/any/sum/per/minute

Under the hood

So what is actually going on when we make an AppStats call?

AppStatsEngine.Publish(new AppStatsEvent().PieChart("LoginFailures").Segment("Unknown User").One());

It creates a standard Wolfpack HealthCheck result message and publishes it with the NServiceBus (NSB) publisher.
  • .Piechart("LoginFailures") just sets the "CheckId" property
  • .Segment("Unknown User") just sets the Tags property
  • .One() just sets the ResultCount property to 1
The Wolfpack instance that receives this NSB message should be configured to republish it via one of its database publishers (SQLite, SqlServer, MongoDb) - this just saves the message to the AgentData table where it is accessible to other Wolfpack plugins like the Geckoboard Data Service....our next port of call.

Other ways of invoking AppStats include...

Creating a timer to record the number of milliseconds it took for an operation to run. The result is automatically published when the timer is disposed
// create a timer to wrap an operation
using (var timer = AppStatsEngine.Time("SomeOperation"))
{
    // do some operation to time here
    // the appstat is automatically published
    // when it is disposed
}

Count N of something. Use this to record the value of something, in the example below we are recording the cost of a new order - this could then be plotted on a linegraph across your entire system or displayed on a geck-0-meter to show max, min and average order cost. Again the information is automatically published for you with this call.
AppStatsEngine.Count(9.99, "OrderCost");
// or to record just one of something...
AppStatsEngine.One("Orders");

Last edited Jul 17, 2011 at 9:01 AM by jimbobdog, version 5