2
votes

Basically, I'm trying to add a logging interceptor for each class that doesn't have a ILogger property or field.

The project I'm working with is build using Monorail 2.1, and the latest Castle Windsor stable release (August 2012, 3.1.0).

Let's say that I have:

[ViewComponentDetails("Followers")]
    public class FollowersComponent : ViewComponent
    {
        private readonly ISession NHibernateSession;

        public FollowersComponent(ISession session)
        {
            NHibernateSession = session;
        }

        public override void Render()
        {
            IPrincipal loggedUser = EngineContext.CurrentUser;
            if ((loggedUser.Identity.AuthenticationType == "Monorail Type") && (loggedUser.Identity.IsAuthenticated))
            {
                var user = (PrincipalAdapter<Int64>)Session["User"];
                PropertyBag["loggedUser"] = NHibernateSession.Get<User>(user.UserId);
                RenderView("Followers");

            }
        }
    }

And I want to add to this class, and any other class which doesn't have logging, the following interceptor:

public class AspectLogger : IInterceptor
    {
        public AspectLogger(ILoggerFactory loggerFactory)
        {
            LoggerFactory = loggerFactory;
            Loggers = new ThreadSafeDictionary<Type, ILogger>();
        }

        public ILoggerFactory LoggerFactory { get; private set; }

        public ThreadSafeDictionary<Type, ILogger> Loggers { get; private set; }

        public void Intercept(IInvocation invocation)
        {
            if (!Loggers.ContainsKey(invocation.TargetType))
            {
                Loggers.Add(invocation.TargetType, LoggerFactory.Create(invocation.TargetType));
            }
            ILogger logger = Loggers[invocation.TargetType];

            if (logger.IsDebugEnabled) logger.Debug(invocation.PrettyPrint());
            try
            {
                invocation.Proceed();
            }
            catch (Exception ex)
            {
                if (logger.IsErrorEnabled) logger.Error(invocation.PrettyPrint(), ex);
                throw;
            }
        }
    }

So, using the docs from Windsor's site, and some other blogposts, I have come up with the following installers and facilities: a Nhibernate Installer/Facility which setups the session factory and session, a Monorail installer which adds the Monorail Windsor Facility and registers all the controllers, view components and filters and finally, a logging Installer which add this facility:

public class ExtendedLoggingFacility : LoggingFacility
    {
        public ExtendedLoggingFacility()
            : base()
        {
        }

        public ExtendedLoggingFacility(LoggerImplementation loggingApi) :
            base(loggingApi)
        {
        }

        public ExtendedLoggingFacility(LoggerImplementation loggingApi, string configFile) :
            base(loggingApi, configFile)
        {
        }

        public ExtendedLoggingFacility(string customLoggerFactory, string configFile) :
            base(customLoggerFactory, configFile)
        {
        }

        public ExtendedLoggingFacility(LoggerImplementation loggingApi, string customLoggerFactory, string configFile) :
            base(loggingApi, customLoggerFactory, configFile)
        {
        }

        protected override void Init()
        {
            base.Init();
            Kernel.Register(Component.For<IInterceptor>()
                .ImplementedBy<AspectLogger>());
            Kernel.ComponentRegistered += Kernel_ComponentRegistered;
        }

        private void Kernel_ComponentRegistered(string key, IHandler handler)
        {
            if (!(handler.ComponentModel.Implementation.GetProperties().Any(prop => prop.PropertyType.GetInterfaces().Contains(typeof(ILogger))) || handler.ComponentModel.Implementation.GetFields().Any(l=>l.FieldType.GetInterfaces().Contains(typeof(ILogger)))))
            {
                handler.ComponentModel.Interceptors.AddIfNotInCollection(new InterceptorReference(typeof(AspectLogger)));
            }
        }
    }

The only problem? No interceptor gets registered! Atleast, according to this unit test:

[TestFixture]
    public class ControllersInstallerTests
    {
        private IWindsorContainer containerWithControllers;

        [SetUp]
        public void OnSetup()
        {
            containerWithControllers = new WindsorContainer()
                        .Install(FromAssembly.Containing(typeof(UserController)));
        }

 [Test]
        public void All_components_which_dont_have_ILogger_as_a_property_or_field_have_an_AspectLog()
        {
            var allComponents = GetPublicClassesFromApplicationAssembly(p => !(p.GetProperties().Any(prop => prop.PropertyType.IsAssignableFrom(typeof(ILogger)) || p.GetFields().Any(g => g.FieldType.IsAssignableFrom(typeof(ILogger))))));
            var allHandlers = GetAllHandlers(containerWithControllers);
            foreach (var comp in allComponents)
            {
                var handlersForComponent = GetHandlersFor(comp, containerWithControllers);
                if (handlersForComponent.Length != 0)
                    Assert.True(handlersForComponent.All(p => p.ComponentModel.HasInterceptors));
            }
        }
1

1 Answers

1
votes

Try changing your facility to this:

public class ExtendedLoggingFacility : LoggingFacility
{
    public ExtendedLoggingFacility()
        : base()
    {
    }

    public ExtendedLoggingFacility(LoggerImplementation loggingApi) :
        base(loggingApi)
    {
    }

    public ExtendedLoggingFacility(LoggerImplementation loggingApi, string configFile) :
        base(loggingApi, configFile)
    {
    }

    public ExtendedLoggingFacility(string customLoggerFactory, string configFile) :
        base(customLoggerFactory, configFile)
    {
    }

    public ExtendedLoggingFacility(LoggerImplementation loggingApi, string customLoggerFactory, string configFile) :
        base(loggingApi, customLoggerFactory, configFile)
    {
    }

    protected override void Init()
    {
        base.Init();
        Kernel.Register(Component.For<AspectLogger>());
        Kernel.ComponentModelCreated += Kernel_ComponentModelCreated;
    }

    private void Kernel_ComponentModelCreated(ComponentModel model)
    {
        if (
            !(model.Implementation.GetProperties()
                     .Any(prop => prop.PropertyType.GetInterfaces().Contains(typeof(ILogger))) ||
              model.Implementation.GetFields()
                     .Any(l => l.FieldType.GetInterfaces().Contains(typeof(ILogger)))))
        {
            model.Interceptors.AddIfNotInCollection(InterceptorReference.ForType<AspectLogger>());
        }
    }
}

Also, you should use a IContributeComponentModelConstruction instead: http://docs.castleproject.org/Default.aspx?Page=ComponentModel-construction-contributors&NS=Windsor&AspxAutoDetectCookieSupport=1

It would look like this:

public class ExtendedLoggingFacility : LoggingFacility
{
    public ExtendedLoggingFacility()
        : base()
    {
    }

    public ExtendedLoggingFacility(LoggerImplementation loggingApi) :
        base(loggingApi)
    {
    }

    public ExtendedLoggingFacility(LoggerImplementation loggingApi, string configFile) :
        base(loggingApi, configFile)
    {
    }

    public ExtendedLoggingFacility(string customLoggerFactory, string configFile) :
        base(customLoggerFactory, configFile)
    {
    }

    public ExtendedLoggingFacility(LoggerImplementation loggingApi, string customLoggerFactory, string configFile) :
        base(loggingApi, customLoggerFactory, configFile)
    {
    }

    protected override void Init()
    {
        base.Init();
        Kernel.Register(Component.For<AspectLogger>());
        Kernel.ComponentModelBuilder.AddContributor(new AddLoggingAspect());
    }
}

public class AddLoggingAspect : IContributeComponentModelConstruction
{
    public void ProcessModel(IKernel kernel, ComponentModel model)
    {
        if (
            !(model.Implementation.GetProperties()
                   .Any(prop => prop.PropertyType.GetInterfaces().Contains(typeof(ILogger))) ||
              model.Implementation.GetFields()
                   .Any(l => l.FieldType.GetInterfaces().Contains(typeof(ILogger)))))
        {
            model.Interceptors.AddIfNotInCollection(InterceptorReference.ForType<AspectLogger>());
        }
    }
}