1

Surely other people have gotten these two to work together. But for some reason I can't. I'm relatively new to Castle, but I've followed their explanation of how to add it. Castle was built with log4net 1.2.10, so I had to add a binding redirect to get it to work with 1.2.11.

In my code, the failure happens in the logger installer:

public class LoggerInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.AddFacility<LoggingFacility>(f => f.UseLog4Net());
    }
}

I get a MissingMethodException for:

void log4net.Config.XmlConfigurator.ConfigureAndWatch(System.IO.FileInfo)

I don't know if log4net has changed, but the signature for ConfigureAndWatch is:

ICollection log4net.Config.XmlConfigurator.ConfigureAndWatch(System.IO.FileInfo)

So I see what the problem is, but I can't seem to find anything about this problem and I can't imagine I'm the only person who's run into it.

Pete
  • 6,585
  • 5
  • 43
  • 69

3 Answers3

4

I know this is an old question, but I just wanted to add an alternative solution, in case anyone is struggling with the same issue. I solved it without adding any binding redirects and without referencing two versions of log4net.

First make sure to uninstall the following packages (the latter has a hard dependency on log4net 1.2.10 and we want to use the newest log4net version):

Uninstall-Package Castle.Windsor-log4net
Uninstall-Package Castle.Core-log4net

Make sure you have the LoggingFacility and log4net installed (replace the log4net version with the desired version):

Install-Package Castle.LoggingFacility
Install-Package log4net -Version 1.2.13

Implement the AbstractLoggerFactory:

public class Log4NetFactory : Castle.Core.Logging.AbstractLoggerFactory
{
    internal const string DefaultConfigFileName = "log4net.config";

    public Log4NetFactory()
        : this(DefaultConfigFileName)
    {
    }

    public Log4NetFactory(string configFile)
    {
        var file = GetConfigFile(configFile);
        XmlConfigurator.ConfigureAndWatch(file);
    }

    public Log4NetFactory(bool configuredExternally)
    {
        if (configuredExternally)
        {
            return;
        }

        var file = GetConfigFile(DefaultConfigFileName);
        XmlConfigurator.ConfigureAndWatch(file);
    }

    public Log4NetFactory(Stream config)
    {
        XmlConfigurator.Configure(config);
    }

    public override ILogger Create(Type type)
    {
        if (type == null)
            throw new ArgumentNullException("type");
        var log = LogManager.GetLogger(type);
        return new Log4netLogger(log, this);
    }

    public override ILogger Create(Type type, LoggerLevel level)
    {
        throw new NotSupportedException("Logger levels cannot be set at runtime. Please review your configuration file.");
    }

    public override ILogger Create(string name)
    {
        if (name == null)
            throw new ArgumentNullException("name");
        var log = LogManager.GetLogger(name);
        return new Log4netLogger(log, this);
    }

    public override ILogger Create(string name, LoggerLevel level)
    {
        throw new NotSupportedException("Logger levels cannot be set at runtime. Please review your configuration file.");
    }
}

Implement the ILogger interface:

[Serializable]
public class Log4netLogger : MarshalByRefObject, Castle.Core.Logging.ILogger
{
    private static readonly Type DeclaringType = typeof(Log4netLogger);

    public Log4netLogger(ILogger logger, Log4NetFactory factory)
    {
        Logger = logger;
        Factory = factory;
    }

    internal Log4netLogger()
    {
    }

    internal Log4netLogger(ILog log, Log4NetFactory factory)
        : this(log.Logger, factory)
    {
    }

    public bool IsDebugEnabled
    {
        get { return Logger.IsEnabledFor(Level.Debug); }
    }

    public bool IsErrorEnabled
    {
        get { return Logger.IsEnabledFor(Level.Error); }
    }

    public bool IsFatalEnabled
    {
        get { return Logger.IsEnabledFor(Level.Fatal); }
    }

    public bool IsInfoEnabled
    {
        get { return Logger.IsEnabledFor(Level.Info); }
    }

    public bool IsWarnEnabled
    {
        get { return Logger.IsEnabledFor(Level.Warn); }
    }

    protected internal Log4NetFactory Factory { get; set; }

    protected internal ILogger Logger { get; set; }

    public override string ToString()
    {
        return Logger.ToString();
    }

    public virtual Castle.Core.Logging.ILogger CreateChildLogger(string name)
    {
        return Factory.Create(Logger.Name + "." + name);
    }

    public void Debug(string message)
    {
        if (IsDebugEnabled)
        {
            Logger.Log(DeclaringType, Level.Debug, message, null);
        }
    }

    public void Debug(Func<string> messageFactory)
    {
        if (IsDebugEnabled)
        {
            Logger.Log(DeclaringType, Level.Debug, messageFactory.Invoke(), null);
        }
    }

    public void Debug(string message, Exception exception)
    {
        if (IsDebugEnabled)
        {
            Logger.Log(DeclaringType, Level.Debug, message, exception);
        }
    }

    public void DebugFormat(string format, params Object[] args)
    {
        if (IsDebugEnabled)
        {
            Logger.Log(DeclaringType, Level.Debug, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), null);
        }
    }

    public void DebugFormat(Exception exception, string format, params Object[] args)
    {
        if (IsDebugEnabled)
        {
            Logger.Log(DeclaringType, Level.Debug, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), exception);
        }
    }

    public void DebugFormat(IFormatProvider formatProvider, string format, params Object[] args)
    {
        if (IsDebugEnabled)
        {
            Logger.Log(DeclaringType, Level.Debug, new SystemStringFormat(formatProvider, format, args), null);
        }
    }

    public void DebugFormat(Exception exception, IFormatProvider formatProvider, string format, params Object[] args)
    {
        if (IsDebugEnabled)
        {
            Logger.Log(DeclaringType, Level.Debug, new SystemStringFormat(formatProvider, format, args), exception);
        }
    }

    public void Error(string message)
    {
        if (IsErrorEnabled)
        {
            Logger.Log(DeclaringType, Level.Error, message, null);
        }
    }

    public void Error(Func<string> messageFactory)
    {
        if (IsErrorEnabled)
        {
            Logger.Log(DeclaringType, Level.Error, messageFactory.Invoke(), null);
        }
    }

    public void Error(string message, Exception exception)
    {
        if (IsErrorEnabled)
        {
            Logger.Log(DeclaringType, Level.Error, message, exception);
        }
    }

    public void ErrorFormat(string format, params Object[] args)
    {
        if (IsErrorEnabled)
        {
            Logger.Log(DeclaringType, Level.Error, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), null);
        }
    }

    public void ErrorFormat(Exception exception, string format, params Object[] args)
    {
        if (IsErrorEnabled)
        {
            Logger.Log(DeclaringType, Level.Error, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), exception);
        }
    }

    public void ErrorFormat(IFormatProvider formatProvider, string format, params Object[] args)
    {
        if (IsErrorEnabled)
        {
            Logger.Log(DeclaringType, Level.Error, new SystemStringFormat(formatProvider, format, args), null);
        }
    }

    public void ErrorFormat(Exception exception, IFormatProvider formatProvider, string format, params Object[] args)
    {
        if (IsErrorEnabled)
        {
            Logger.Log(DeclaringType, Level.Error, new SystemStringFormat(formatProvider, format, args), exception);
        }
    }

    public void Fatal(string message)
    {
        if (IsFatalEnabled)
        {
            Logger.Log(DeclaringType, Level.Fatal, message, null);
        }
    }

    public void Fatal(Func<string> messageFactory)
    {
        if (IsFatalEnabled)
        {
            Logger.Log(DeclaringType, Level.Fatal, messageFactory.Invoke(), null);
        }
    }

    public void Fatal(string message, Exception exception)
    {
        if (IsFatalEnabled)
        {
            Logger.Log(DeclaringType, Level.Fatal, message, exception);
        }
    }

    public void FatalFormat(string format, params Object[] args)
    {
        if (IsFatalEnabled)
        {
            Logger.Log(DeclaringType, Level.Fatal, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), null);
        }
    }

    public void FatalFormat(Exception exception, string format, params Object[] args)
    {
        if (IsFatalEnabled)
        {
            Logger.Log(DeclaringType, Level.Fatal, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), exception);
        }
    }

    public void FatalFormat(IFormatProvider formatProvider, string format, params Object[] args)
    {
        if (IsFatalEnabled)
        {
            Logger.Log(DeclaringType, Level.Fatal, new SystemStringFormat(formatProvider, format, args), null);
        }
    }

    public void FatalFormat(Exception exception, IFormatProvider formatProvider, string format, params Object[] args)
    {
        if (IsFatalEnabled)
        {
            Logger.Log(DeclaringType, Level.Fatal, new SystemStringFormat(formatProvider, format, args), exception);
        }
    }

    public void Info(string message)
    {
        if (IsInfoEnabled)
        {
            Logger.Log(DeclaringType, Level.Info, message, null);
        }
    }

    public void Info(Func<string> messageFactory)
    {
        if (IsInfoEnabled)
        {
            Logger.Log(DeclaringType, Level.Info, messageFactory.Invoke(), null);
        }
    }

    public void Info(string message, Exception exception)
    {
        if (IsInfoEnabled)
        {
            Logger.Log(DeclaringType, Level.Info, message, exception);
        }
    }

    public void InfoFormat(string format, params Object[] args)
    {
        if (IsInfoEnabled)
        {
            Logger.Log(DeclaringType, Level.Info, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), null);
        }
    }

    public void InfoFormat(Exception exception, string format, params Object[] args)
    {
        if (IsInfoEnabled)
        {
            Logger.Log(DeclaringType, Level.Info, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), exception);
        }
    }

    public void InfoFormat(IFormatProvider formatProvider, string format, params Object[] args)
    {
        if (IsInfoEnabled)
        {
            Logger.Log(DeclaringType, Level.Info, new SystemStringFormat(formatProvider, format, args), null);
        }
    }

    public void InfoFormat(Exception exception, IFormatProvider formatProvider, string format, params Object[] args)
    {
        if (IsInfoEnabled)
        {
            Logger.Log(DeclaringType, Level.Info, new SystemStringFormat(formatProvider, format, args), exception);
        }
    }

    public void Warn(string message)
    {
        if (IsWarnEnabled)
        {
            Logger.Log(DeclaringType, Level.Warn, message, null);
        }
    }

    public void Warn(Func<string> messageFactory)
    {
        if (IsWarnEnabled)
        {
            Logger.Log(DeclaringType, Level.Warn, messageFactory.Invoke(), null);
        }
    }

    public void Warn(string message, Exception exception)
    {
        if (IsWarnEnabled)
        {
            Logger.Log(DeclaringType, Level.Warn, message, exception);
        }
    }

    public void WarnFormat(string format, params Object[] args)
    {
        if (IsWarnEnabled)
        {
            Logger.Log(DeclaringType, Level.Warn, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), null);
        }
    }

    public void WarnFormat(Exception exception, string format, params Object[] args)
    {
        if (IsWarnEnabled)
        {
            Logger.Log(DeclaringType, Level.Warn, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), exception);
        }
    }

    public void WarnFormat(IFormatProvider formatProvider, string format, params Object[] args)
    {
        if (IsWarnEnabled)
        {
            Logger.Log(DeclaringType, Level.Warn, new SystemStringFormat(formatProvider, format, args), null);
        }
    }

    public void WarnFormat(Exception exception, IFormatProvider formatProvider, string format, params Object[] args)
    {
        if (IsWarnEnabled)
        {
            Logger.Log(DeclaringType, Level.Warn, new SystemStringFormat(formatProvider, format, args), exception);
        }
    }
}

Register the custom Log4NetFactory in a WindsorInstaller:

public class LoggerInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.AddFacility<LoggingFacility>(x =>
            x.WithConfig(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile)
            .ToLog("MyLogger")
            .LogUsing<Log4NetFactory>());
    }
}

Add the usual log4net configuration to your app/web.config - if not already there.

Make sure that "MyLogger" exists in the log4net configuration, e.g.

<logger name="MyLogger" additivity="false">
  <level value="DEBUG" />
  <appender-ref ref="MyAppender" />
</logger>

Now you can have the ILogger injected like this:

public class MyClass
{
    public MyClass(ILogger logger)
    {
        logger.Info("Castle Windsor with newest log4net.");
    }
}

Using the above method you can use any version of log4net together with Castle Windsor. I have log4net 1.2.13 working with Castle Windsor 3.3.3.

sb.olofsson
  • 899
  • 9
  • 14
2

I had to configure application to use both versions of log4net:

<configuration>
  <runtime>
     <assemblyBinding>
      <dependentAssembly>
        <assemblyIdentity name="log4net" publicKeyToken="669e0ddf0bb1aa2a" />
        <codeBase version="1.2.11.0" href="..\packages\log4net.2.0.0\lib\net40-full\log4net.dll" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="log4net" publicKeyToken="1b44e1d426115821" />
        <codeBase version="1.2.10.0" href="..\packages\log4net.1.2.10\lib\2.0\log4net.dll" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

I don't know if there is better way to make it work, but this works.

Aleš Roubíček
  • 5,198
  • 27
  • 29
  • Thanks. That's what I had to do. – Pete Oct 17 '12 at 12:25
  • It will work on dev/build machine. It is necessary to handle deployment time scenario (both libs should be part of deployed application and config should be transformed to reflect actual paths). – Aleš Roubíček Oct 18 '12 at 06:54
1

Log4Net changes their public key token for minor releases, which can be annoying and has caused many folk grief. Check your config and read the following question and answer

how do I work around log4net keeping changing publickeytoken

This may not be your problem or only problem but when I ran into similar trouble upgrading castle and log4net and this was my major grief.

Community
  • 1
  • 1
dove
  • 20,469
  • 14
  • 82
  • 108
  • I had already seen that one which is what led to the binding redirect I mentioned. Prior to that, it balked at the version numbers. But I'm using the old key version of log4net. The problem isn't the key. It's the method signature. Additionally, calling Castle's ConfiguredExternally() doesn't appear to fix this either. – Pete Oct 16 '12 at 21:34