One of my projects is an ASP.Net MVC 4 website. While building it, I realized it would be useful to see what users do on the site.
Why you want logging of the user actions
- You can discover functions that your users never use – which may mean you need to make them more visible, or maybe remove them.
- You can find which actions are the most commonly used ones – which tells you what actions your users find most useful.
- You can see if your users always call one action immediately after another – which may mean you can combine those actions on one page (making it look like less steps to your users).
- It can show you which page is the most frequent one that users visit last – which may tell you that you need to simplify it, if users (or potential users) are abandoning your site immediately after seeing that page.
That information is extremely useful for determining what areas to work on to get new users, and keep existing users.
How I built my logging for ASP.Net MVC 4
I looked at some of the common logging libraries, and they seemed like more work to setup than to write on my own. So I decided to write my own.
Here’s the code I’m using to create a log of user activity.
First, you need to add a value to the user’s session variable. Apparently, if you don’t add at least one value, IIS may change your user’s Session.SessionID while they’re still in the same session.
Since I need the key value in a couple places, I added it to a static Constants class in my project. That makes it easy to ensure I don’t mistype the key somewhere in the code.
Constants.cs
using System.Collections.Generic; namespace MyApp { public static class Constants { public const string SESSION_ACTIVITY_LOG_ID = "__SessionActivityLog"; } }
Next, add a value to the user’s session variable in the Global.asax.cs file. Place this code in the Session_Start method (or create the method, if it doesn’t already exist).
Global.asax.cs
void Session_Start(object sender, EventArgs e) { HttpContext.Current.Session.Add(Constants.SESSION_ACTIVITY_LOG_ID, Guid.NewGuid().ToString()); }
Now we need to create something to record the user’s action to the log. The simplest way to do this is to create a custom attribute.
Here’s the attribute I wrote – LogSessionActivityAttribute.cs.
This attribute checks if the session has a value set for the activity log ID. That value should always exist, but I’d rather be safe and not worry about having a null exception thrown from my logging code.
If the ID exists, the attribute calls RecordPageAccess() – my logging code. RecordPageAccess writes the value of the session ID, user name, controller, and action (along with the current date/time) to a database table.
EDIT (24 April 2014): For me, SessionActionLogAccessor is a static class I created, and RecordPageAccess() does an insert of the logging values into a database table. You’ll need to create your own version of this class and method to save the logging information where you want it. Some options besides the database are a text file, an XML file, or a web service. Just remember that wherever you do the logging, make the method asynchronous, or the logging can slow down your website’s response time.
The logging is asynchronous. There’s no need to make the user wait for a database insert that isn’t returning any values. I also have the logging wrapped in a try…catch block that ignores any exceptions. If the logging has an error, the users shouldn’t see any alerts about it.
LogSessionActivityAttribute.cs
using System.Web.Mvc; using MyApp.DataAccessors; namespace MyApp.CustomAttributes { public class LogSessionActivityAttribute : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { if(filterContext.HttpContext.Session[Constants.SESSION_ACTIVITY_LOG_ID] != null) { try { SessionActionLogAccessor.RecordPageAccess( filterContext.HttpContext.Session[Constants.SESSION_ACTIVITY_LOG_ID].ToString(), filterContext.HttpContext.User.Identity.Name, filterContext.Controller.ToString(), filterContext.ActionDescriptor.ActionName); } catch { // Don't raise any exceptions if the logging fails. } } base.OnActionExecuting(filterContext); } } }
Now that the attribute exists, we need to have it called before every action.
The easiest way to do this in MVC is to add it to the App_Start\FilterConfig.cs class. The RegisterGlobalFilters lets you add any attributes you want executed for every action. This saves you from needing to manually add it as an attribute before each action.
Here’s my RegisterGlobalFilters method, after adding the filter for this new logging attribute.
FilterConfig.cs
using System.Web.Mvc; using MyApp.CustomAttributes; namespace MyApp { public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new AuthorizeAttribute()); filters.Add(new LogSessionActivityAttribute()); } } }
That’s it.
Now you’ll have a table showing you a log of the actions your users run when they use your ASP.Net MVC 4 website. This information will let you see what areas of the website need improvements, and if the improvements actually improve the user experience or retention.
28 Comments