views:

122

answers:

2

i'm trying to work out the best method to perform logging in the application i'm currently developing.

right now, i have a Log table that stores the username, timestamp, action, controller, and a message. when a controller is instantiated, it gets the IoC info through Castle Windsor.

for example, my "Sites" controller is created as follows:

    private ISitesRepository siteRepository;
    private ILogWriter logWriter;

    public SiteController(ISitesRepository siteRepository, ILogWriter logWriter)
    {
        this.siteRepository = siteRepository;
        this.logWriter = logWriter;
    }

and the log writer has a function that creates and inserts a log entry (WriteToLog). within the Sites controller's Edit and Create actions, it calls the WriteToLog function.

this is working and doing its job, but my question is- do i really need to set up each controller this way, passing through the ILogWriter interface/repository? it struck me that i could possibly set up a LogController, and just have that do the "heavy lifting" of writing to my logs.

that way, i wouldn't have to mess with the IoC stuff in every other controller. is it possible to execute an action on another controller (for example, a LogController-> WriteLog)? i'm not sure how would that be done without doing a redirect...

+1  A: 

Could you pass by an abstract class? This abstract class having a static property referencing you log writer?

something like this

public abstract class BaseController
{
    public static ILogWriter Logwriter{get;set;}
    public static BaseController
    {
        Logwriter = YourFactory.GetLogwriter();
    }
}

public class YourController:BaseController
{
    public YourController(ISitesRepository siteRepository)
    {
    }
}
Gregoire
+1  A: 

Ok, after much head scratching, i think i found an acceptable solution.

I implemented my logging action as a custom action filter as so:

public class LogAction : ActionFilterAttribute, IActionFilter
{
    public LogLevel loglevel;
    public string message;


    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        ILogWriter logWriter = AppServiceFactory.Instance.Create<ILogWriter>();

        logWriter.WriteToLog(
          filterContext.ActionDescriptor.ControllerDescriptor.ControllerName, 
          filterContext.ActionDescriptor.ActionName,
          loglevel,
          filterContext.HttpContext.Timestamp,
          filterContext.HttpContext.User.Identity.Name.ToString(),
          message + "(id=" + filterContext.RouteData.Values["id"] + ")");
    }

}

but i ran into a wall trying to get the IoC to work in a custom attribute filter. scouring stackoverflow and google searches, i found that it's sort of difficult to do, with talk about using different wrappers, action invokers, etc, which all seemed more complicated than i was really willing to deal with.

trying to learn more about IoC (i'm still very new at this), i found this article, which really helped point me in the right direction. i added his sealed AppServiceFactory class with my WindsorControllerFactory, and it worked like a charm.

As i said, i'm very new with to MVC and this IoC stuff, so i'm not sure this is an ideal way of handling things- but it seems simple and it works so far. I'd welcome any comments or criticisms on handling it through this method.

UPDATE Figured out a different way of doing this- created a function in my WebUI project as such:

public static class Loggers
{
    public static void WriteLog(ControllerContext controllerContext, LogLevel logLevel, string message)
    {
        ILogWriter logWriter = AppServiceFactory.Instance.Create<ILogWriter>();
        logWriter.WriteToLog(
            controllerContext.RouteData.Values["controller"].ToString(),
            controllerContext.RouteData.Values["action"].ToString(),
            logLevel,
            controllerContext.HttpContext.Timestamp,
            controllerContext.HttpContext.User.Identity.Name.ToString(),
            message);
    }
}

now, wherever i want to log something, i can call

            Loggers.WriteLog(
            this.ControllerContext,
            LogLevel.Membership,
            "Removed role '" + role + "'" + " from user " + _userService.Get(id).UserName );

to write a record to the log. this gives me a lot more flexibility on my "message" content, and solves the problem of including logging in the global.asax file, which would've been difficult if not impossible using the attribute filters. i'll leave the rest, as it may be of use to someone else, but i think this is the way i'll go on this.

as usual, things are usually simpler in MVC than i original think they will be :)

Jamie M
one comment- the only thing i don't like about this is that i can't customize the "message" field with data that can only be found in the action that causes the filter to trigger. will work on this.
Jamie M