Log4Net with castle windsor

青春壹個敷衍的年華 提交于 2019-12-11 01:46:24

问题


I am configuring logging for my application and for logging I am using log4net and castle windsor for DI.

I want logging framework to be wrap inside custom implementation so it can be changed in future.

public interface ICustomLogger
{
    void Debug(object message, Exception ex = null);
    void Info(object message, Exception ex = null);
    void Warn(object message, Exception ex = null);
    void Error(object message, Exception ex = null);
    void Fatal(object message, Exception ex = null);
}

public class CustomLogger : ICustomLogger
{
    private readonly log4net.ILog _log;
    private readonly log4net.ILog _log1;

    public CustomLogger()
    {
        //approach1
        var stack = new StackTrace();
        var frame = stack.GetFrame(1);
        var method = frame.GetMethod();
        Type type = method.DeclaringType;
        _log = log4net.LogManager.GetLogger(type);

        //approach2
        var dtype = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType;
        _log1 = log4net.LogManager.GetLogger(dtype);
    }

    public CustomLogger(string name)
    {
        _log = log4net.LogManager.GetLogger(name);
    }

    public CustomLogger(Type type)
    {
        _log = log4net.LogManager.GetLogger(type);
    }

    public void Debug(object message, Exception ex = null)
    {
        if (_log.IsDebugEnabled)
        {
            if (ex == null)
            {
                _log.Debug(message);
            }
            else
            {
                _log.Debug(message, ex);
            }
        }
    }

    public void Info(object message, Exception ex = null)
    {
        if (_log.IsInfoEnabled)
        {
            if (ex == null)
            {
                _log.Info(message);
            }
            else
            {
                _log.Info(message, ex);
            }
        }
    }

    public void Warn(object message, Exception ex = null)
    {
        if (_log.IsWarnEnabled)
        {
            if (ex == null)
            {
                _log.Warn(message);
            }
            else
            {
                _log.Warn(message, ex);
            }
        }
    }

    public void Error(object message, Exception ex = null)
    {
        if (_log.IsErrorEnabled)
        {
            if (ex == null)
            {
                _log.Error(message);
            }
            else
            {
                _log.Error(message, ex);
            }
        }
    }

    public void Fatal(object message, Exception ex = null)
    {
        if (_log.IsFatalEnabled)
        {
            if (ex == null)
            {
                _log.Fatal(message);
            }
            else
            {
                _log.Fatal(message, ex);
            }
        }
    }
}

To register this custom implementation with DI...

   container.Register(Component.For<ICustomLogger>()
                                   .ImplementedBy<CustomLogger>()
                                   .LifeStyle.Transient);

Problem comes when I ask DI to resolve logger, then it always return logger for Customlogger type not the class where I want to use it.

class ABC
{
    ICustomLogger _logger;

    public ABC(ICustomLogger logger)
    {
        _logger = logger; // type of this logger is CustomLogger not ABC
    }
}

Both the approach are not working to resolve logger as ABC. Can anyone help me to understand what's wrong here and how to fix the issue.


回答1:


You can do this via a custom dependency resolver.

You first need to create an implementation of ISubDependencyResolver that can resolve dependencies of type ICustomLogger:

public class LoggerResolver : ISubDependencyResolver
{
    public bool CanResolve(
        CreationContext context,
        ISubDependencyResolver contextHandlerResolver,
        ComponentModel model,
        DependencyModel dependency)
    {
        //We can only handle dependencies of type ICustomLogger 
        return dependency.TargetType == typeof (ICustomLogger);
    }

    public object Resolve(
        CreationContext context,
        ISubDependencyResolver contextHandlerResolver,
        ComponentModel model,
        DependencyModel dependency)
    {
        //We pass the requested type, e.g. ABC, to the constructor of CustomLogger
        return new CustomLogger(context.RequestedType);
    }
}

You then need to register this resolver with the container like this:

container.Kernel.Resolver.AddSubResolver(new LoggerResolver());



回答2:


For your specific question - in both approaches you never really leave the "scope" of your class. With the first you are creating a new StackTrace and in the other the declaring type of a constructor is that class itself.

But you implemented a constructor that can receive a type so why not use it. Currently your CustomLogger is registered with your default constructor:

//There is no place here that you tell castle to resolve using the constructor 
//that receives `ABS` 
container.Register(Component.For<ICustomLogger>()
                            .ImplementedBy<CustomLogger>()
                            .LifeStyle.Transient);

See Castle Windsor passing constructor parameters to understand how to pass the parameters and that way invoke the constructor you want


In addition - Worth re-thinking:

Though it is a good idea to create such abstraction between your code and external source in this case I would not do it and I will explain why:

  1. From my experience one doesn't really change the logging framework after the code is up and running. Especially since you are working with a mature and excellent framework - Log4Net. It has many built in abilities and is very adaptable for ones needs: From different formatting of the messaged to outputting the logs to different sources such as databases, files and if I'm not wrong there are also appenders for things like elastic search.
  2. You are using Castle Windsor which has a good integration with Log4Net and has for you a ready made Logging Facility to Log4Net. See this question for how simple it is to add it.
  3. Last point is that if you already write good SOLID code and pass your logger as ILogger to all the components (and not a specific implementation) all they will probably do is call the different Debug/Info/Warn/Error/Fatal methods - which any other mature logging framework will have. So on the day you will have to change (which I think won't happen) you can write an interface that looks like the Log4Net's interface and an implementation that will adapt that to your new logging framework.


来源:https://stackoverflow.com/questions/39909779/log4net-with-castle-windsor

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!