views:

463

answers:

3

Is there A good strategy to Log the User activity on an ASP MVC App? (ActionFilters/ HTTPModules).

Something like last user activity (just like StackOverflow "Seen 23 mins ago"), and even what Pages and Controllers were used, and pushing even further what buttons or links were clicked.

I have ELMAH installed but as far as i know its just for Error Logging.

PD: Google Analytics is not an option.

+4  A: 

You could try using a PostSharp aspect to perform the logging for you, it's multi-cast functionality might come in handy for something like that. If it's not an option, then a Module would probably be the easiest to implement (assuming you can get the required user information at that point in the pipeline).

Rory
Can PostSharp target action methods unobtrusively? I'm thinking it would require attribute decoration, something I know my MVC actionmethods are a little heavy with. Once you get to 4-5 attributes I think things get ugly.
jfar
That was exactly my concern about using an http module, i don't know if i will be able to get the required data in that context.
Omar
A quick google search returned http://www.vyrotek.com/2008/using-postsharp-with-aspnet-mvc/ which seems promising. I haven't taken a look at the code yet, but it may be possible to use the two frameworks together after all.
Rory
+1  A: 

@Rory's idea is excelent. PostSharp is just what you need for this, you might consider coupling it with ASP.net Health Monitoring

stimms
I was looking at PostSharp it look interesting, i was just wondering if it requires a lot of configuration like log4net, or its pretty much straight forward like ELMAH.
Omar
Configuration, no. However it isn't just for logging so you kind of have to roll your own approach to it.
stimms
LAOS (the AOP framework that comes with PostSharp) is designed to work with all sorts of cross-cutting concerns, logging just happens to be one of them (and probably the most used example). The only thing that might be a problem is the interception code that is injected by the post-processor. As jfar commented on my answer, it might cause problems with the way the MVC framework does things, I honestly don't know if they would work together. I suggest you take a look at some of the examples and search around the net to see if someone else has successfully used them together.
Rory
I have used postsharp and MVC together without issue.
stimms
+4  A: 

Action Filter Attributes are perfect for this, just put a call to [YourAttributeName] at the top of your controller (or if you have an Application Controller which your other controllers inherit from, you only need it once in your application).

For example:

namespace the_name_space
{
    [Log]
    public class ApplicationController : Controller
    {

From this point onwards this attribute will be called before each action is run in your controller(s), you can also specify this on just an action by calling [Log] just before it in the same fashion.

To get the logging functionality which you require, you will most likely want to override OnResultExecuting and OnResultExecuted, both of which are pretty self explanatory.

An example of this may go a little like this:

public class LogAttribute : ActionFilterAttribute { protected DateTime start_time;

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        start_time = DateTime.Now;
    }

    public override void OnResultExecuted(ResultExecutedContext filterContext)
    {
        RouteData routeData = filterContext.RouteData;
        TimeSpan duration = (DateTime.Now - start_time);
        string Controller = (string)routeData.Values["controller"];
        string Action = (string)routeData.Values["action"];
        DateTime CreatedAt = DateTime.Now;
        //Save all your required values, including user id and whatnot here.
        //The duration variable will allow you to see expensive page loads on the controller and whatnot, very handy when clients complain about something being slow.
    }
}
Jay
It looks pretty good. But for buttons and links clicked is going to work too?, The idea is that this statistic information is used by the designer to take decisions about what buttons or links are more used and which ones are not event clicked.
Omar
I'm afraid using this method you will not be able to work out which buttons/links are clicked. I don't know of any way of storing this data straight from the interface, but if all buttons/links call an action you could pass through a variable which is picked up in this method, this would unfortunately be time intensive adding to all your links. To do this pass "buttonClicked=name" and access it via `the filterContext.HttpContext.Request.RawUrl` variable. Not the best option though, and will only work for buttons which make a request to the server.
Jay
Probably can't get which buttons or links are clicked, but you could get referring URL, I think, through the ResultExecutedContext?
Kenny Eliasson
You can pass through the button name which was clicked if it's making a connection back to the server, but as mentioned above it'd be pretty horrible actually doing it. Much better idea using the referring URL to find out where the link was clicked as Kenny Eliasson said, I'm not sure off the top of my head about ResultExecutedContext but there'd be a way of doing it. Nice idea Kenny
Jay