How to log your user’s controller actions for an ASP.Net MVC 4 website

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

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

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

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

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.

20 thoughts on “How to log your user’s controller actions for an ASP.Net MVC 4 website

  1. Hi

    I am trying to implement this in my system however in my filterconfig file It can’t seem to find LogSessionActivityAttribute, I am using mvc 5, any suggestions?

    Anders

    1. Hello Anders,

      You need to add the new class named LogSessionActivityAttribute.cs. The source code for that new class is listed in the blog post above.

    1. SessionActionLogAccessor is something you’ll have to write on your own, depending on how and where you want to record the users’ action.

      For me, it’s a static class. The RecordPageAccess method inserts a new row with the parameter values into the database. You could also write them to a text file, send to a web service you write, or maybe you have some other need.

      One thing you want to remember when you write your RecordPageAccess method is to make the method asynchronous. That way, your application won’t be waiting for a response, making things slower for your users.

  2. Huge thanks! 🙂 This is a very clever idea for log reporting… There is one question though! this method cannot differentiate between types of user events (resulted by actions) (login related attempts, url requesting events, etc)

    1. You’re welcome. To do deeper reporting, you’d need to create a table that lists each controller/action combination and defines what “type” it is – so, HomeController/Login would be a “Login” event.

      On my [too long] “To Do” list, I want to expand this idea into a library that would let you get deeper analytics from your MVC web app. It would give you more insight into what your users are really doing with your site, and where it may have problems.

  3. Thanks Really Good Idea.
    I have some questions. HandleErrorAndLogAttribute() is have error for me . İt is not in default context . (in filterConfig.cs) and I have authorization problem when implemented this code. IIS cant authorizate session.
    Thanks again…

    1. The HandleErrorAndLogAttribute is another custom attribute I created in that solution to log error messages to the database. You can remove that line from your FilterConfig.cs file. If you aren’t using authorization, you can probably also remove the line with “filters.Add(new AuthorizeAttribute());”

      Let me know if that doesn’t solve the problem.

    1. You’ll need to create your own SessionActionLogAccessor. I don’t know where you want to do your logging (file, database, web service, Windows events, etc.), or what else you’re doing in your application (using Entity Framework, using a different ORM, using a logging library, etc.).

      When I wrote this for my application, SessionActionLogAccessor was a static class. The RecordPageAccess method did an asynchronous insert into the database.

    1. Hello Sam,

      I’m not sure how to do that. It’s been a long time since I worked on an ASP.NET/MVC application. I think you might be able to do this if you created a custom attribute, which would get the current entity, and pass it as a parameter to the attribute. But that would probably require you to manually add the attribute before each action.

      If someone else sees this message, and has a suggestion, please leave a comment.

  4. I have some question.

    I have table ASPNET User Identity , patient, and appointment.

    Patient need some username to store table User and I can log in to the website.

    but the problem is, if I want to create appointment with username log in and I can’t store username on table user to table appointment in patient_id column.

    how to figure that ?

    Help me Scott. Thanks

    1. Hello Julius,

      I am having difficulty understanding the problem. Can you show the database table structure, and some example data? That might help me understand better.

  5. Thank you for your very helpful guide.  Successfully implemented a modified version in MVC5 based on your code, logging data by overriding OnActionExecuted, so that values of session variables changed by the action could also be logged.

Leave a Reply

Your email address will not be published. Required fields are marked *