4
votes

We're trying to wrap log4net inside of some sort of wrapper, be it an injectable class or a static method wrapper. We're trying to avoid having to declare this on every class that requires logging:

private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

This approach PERFORMS well, however, as it logs the correct type as well as the method in which it was called.

The problem we're facing is determining a wrapper or some sort that would allow us to abstract away this call - avoiding the ugly declaration for each class - yet still retain the level of detail we need. In this case, when a .Debug(string message) method is called, we want whatever method handling action to allow the underlying log4net instance to record the correct type and method, as it would normally.

In our tests - ranging from a static wrapper to injecting ILog instances - we were unable to obtain both pieces of information.

Is there a proper working example, preferably using StructureMap, to wire this up, or at least some non-IoC method that would abstract away the setup of the log manager?

Thanks.

2
I'm using interception for this.Piotr Perak
What is that? Could you provide an example? Thanks.Ryan Peters
I'm on my phone now. Look for Castle Interceptors. It isn't exactly what you want but it gives me almost what you need without a single call to logging framework in my classes.Piotr Perak

2 Answers

2
votes

Regarding using StructureMap to create your logger on the fly this is how I did it awhile back. (note 'Injected', I explain that next)

namespace EC2Utilities.Common.Factory
{
    public static class ContainerBootstrapper
    {
        public static void BootstrapStructureMap()
        {
            // Initialize the static ObjectFactory container
            ObjectFactory.Initialize(x =>
            {
                x.For<IBackupEngine>().Use<BackupEngine>();
                x.For<IConfigResourceAccess>().Use<ConfigResourceAccess>();
                x.For<IEc2ResourceAccess>().Use<Ec2ResourceAccess>();
                x.For<IScheduleEngine>().Use<ScheduleEngine>();
                x.For<IScheduleManager>().Use<ScheduleManager>();
                x.For<IBackupManager>().Use<BackupManager>();
                x.For<ILog>().Use(y => LogManager.GetLogger("Injected"));
                x.For<IInstanceManager>().Use<InstanceManager>();
                x.RegisterInterceptor(new ResourceAccessTypeInterceptor());
            });
        }
    }
}

With regards to keeping the goodness of using %C.%M in the appender.layout.conversionPattern to get your class and name, the trick is to expose the ILog methods as delegates in the wrapper so the calling class is actually calling the method in ILog rather than your wrapper:

namespace LoggingTest
{
    public delegate void LogFormat(string format, params object[] args);

    public interface ILoggerWrapper
    {
        Action<object> Debug { get; } 

        Action<object, Exception> DebugEx { get; }

        LogFormat DebugFormat { get; }


        Action<object> Info { get; }

        Action<object, Exception> InfoEx { get; }

        LogFormat InfoFormat { get; }


        Action<object> Warn { get; }

        Action<object, Exception> WarnEx { get; }

        LogFormat WarnFormat { get; }


        Action<object> Error { get; }

        Action<object, Exception> ErrorEx { get; }

        LogFormat ErrorFormat { get; }


        Action<object> Fatal { get; }

        Action<object, Exception> FatalEx { get; }

        LogFormat FatalFormat { get; }
    }
}

And the implementation:

namespace LoggingTest
{
    public class LoggerWrapper : ILoggerWrapper
    {
        private readonly ILog _log;

        public LoggerWrapper(ILog log)
        {
            _log = log;
        }     

        public Action<object> Debug { get { return _log.Debug; } }

        public Action<object, Exception> DebugEx { get { return _log.Debug; } }

        public LogFormat DebugFormat { get { return _log.DebugFormat; } }

        public Action<object> Info { get { return _log.Info; } }

        public Action<object, Exception> InfoEx { get { return _log.Info; } }

        public LogFormat InfoFormat { get { return _log.InfoFormat; } }

        public Action<object> Warn { get { return _log.Warn; } }

        public Action<object, Exception> WarnEx { get { return _log.Warn; } }

        public LogFormat WarnFormat { get { return _log.WarnFormat; } }

        public Action<object> Error { get { return _log.Error; } }

        public Action<object, Exception> ErrorEx { get { return _log.Error; } }

        public LogFormat ErrorFormat { get { return _log.ErrorFormat; } }

        public Action<object> Fatal { get { return _log.Fatal; } }

        public Action<object, Exception> FatalEx { get { return _log.Fatal; } }

        public LogFormat FatalFormat { get { return _log.FatalFormat; } }
    }
}

You would of course also need to register ILoggerWrapper in the container as well.

HTH, Eric

1
votes

As for non-DI approach, you can easily separate out log4net globally:

http://www.wiktorzychla.com/2010/09/easy-log4net-integration-into-net.html

You could even make such separation a wrapper over common.logging so that you could inject any logging subsystem transparently.