带温莎城堡的 Log4Net

Log4Net with castle windsor

我正在为我的应用程序配置日志记录,我正在为 DI 使用 log4netcastle windsor

我希望将日志记录框架包装在自定义实现中,以便将来可以更改。

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);
            }
        }
    }
}

要向 DI 注册此自定义实现...

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

当我要求 DI 解析记录器时出现问题,然后它总是 return Customlogger 类型的记录器,而不是我想使用它的 class。

class ABC
{
    ICustomLogger _logger;

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

这两种方法都无法将记录器解析为 ABC。 谁能帮助我了解这里出了什么问题以及如何解决这个问题。

您可以通过自定义 dependency resolver

您首先需要创建 ISubDependencyResolver 的实现,它可以解析类型 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);
    }
}

然后您需要像这样向容器注册此解析器:

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

对于您的具体问题 - 在这两种方法中,您永远不会真正离开 class 的 "scope"。第一个是创建一个新的 StackTrace,另一个是构造函数的声明类型是 class 本身。

但是您实现了一个可以接收类型的构造函数所以为什么不使用它。当前您的 CustomLogger 已注册到您的默认构造函数:

//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);

请参阅 Castle Windsor passing constructor parameters 以了解如何传递参数以及如何调用所需的构造函数


In addition - Worth re-thinking:

尽管在这种情况下在您的代码和外部源之间创建这种抽象是个好主意,但我不会这样做,我将解释原因:

  1. 根据我的经验,在代码启动 运行 后,并没有真正改变日志框架。特别是因为您正在使用成熟且优秀的框架 - Log4Net。它具有许多内置功能,非常适合各种需求:从消息的不同格式到将日志输出到不同的源,例如数据库、文件,如果我没记错的话,还有用于弹性搜索之类的附加程序。
  2. 您正在使用 Castle Windsor,它与 Log4Net 有很好的集成,并为您准备了 Logging FacilityLog4Net。请参阅 this question 以了解添加它是多么简单。
  3. 最后一点是,如果您已经编写了良好的 SOLID 代码并将您的记录器作为 ILogger 传递给所有组件(而不是特定的实现),它们可能会做的就是调用不同的 Debug/Info/Warn/Error/Fatal 方法——任何其他成熟的日志框架都会有。所以在你必须改变的那一天(我认为这不会发生)你可以编写一个看起来像 Log4Net 的接口和一个将适应你的新日志框架的实现。