【问题标题】:How to use log4net with Dependency Injection如何将 log4net 与依赖注入一起使用
【发布时间】:2009-06-02 17:32:48
【问题描述】:

我正在尝试通过依赖注入框架找出 log4net 的正确模式和用法。

Log4Net 使用 ILog 接口但需要我调用

LogManager.GetLogger(Reflection.MethodBase.GetCurrentMethod().DeclaringType)

在我需要记录信息的每个类或方法中。这似乎违反了 IoC 原则,并且使我不得不使用 Log4Net。

我应该以某种方式在某处放置另一层抽象吗?

另外,我需要像这样记录当前用户名等自定义属性:

log4net.ThreadContext.Properties["userName"] = ApplicationCache.CurrentUserName;

我怎样才能封装它,这样我就不必每次都记住这样做,并且仍然保持当前正在记录的方法。我应该做这样的事情还是我完全错过了标记?

public static class Logger
{
    public static void LogException(Type declaringType, string message, Exception ex)
    {
        log4net.ThreadContext.Properties["userName"] = ApplicationCache.CurrentUserName;
        ILog log = LogManager.GetLogger(declaringType);
        log.Error(message, ex);
    }
}

【问题讨论】:

  • 更有趣的问题是您使用的是哪个 DI 容器?

标签: design-patterns dependency-injection log4net


【解决方案1】:

我认为您在这里只见树木不见森林。 ILog 和 LogManager 是一个轻量级外观,几乎 1:1 等效于 Apache commons-logging,实际上并不会将您的代码耦合到 log4net 的其余部分。

<rant>
我还发现几乎总是,当有人围绕 log4net 创建 MyCompanyLogger 包装器时,他们会严重错失重点,要么失去框架的重要和有用的功能,丢弃有用的信息,失去可能的性能提升甚至使用简化的 ILog 接口,或以上所有接口。换句话说,包装 log4net 以避免与其耦合是一种反模式
</rant>

如果您觉得需要注入它,请通过属性使您的记录器实例可访问以启用注入,但以老式方式创建默认实例。

至于在每条日志消息中包含上下文状态,您需要添加一个全局属性,其 ToString() 解析为您要查找的内容。例如,对于当前堆大小:

public class TotalMemoryProperty
{
    public override string ToString()
    {
        return GC.GetTotalMemory(false).ToString();
    }
}

然后在启动时插入:

GlobalContext.Properties["TotalMemory"] = new TotalMemoryProperty();

【讨论】:

  • 你的咆哮在这里太离谱了。这个人在问如何将 ILog 的实例注入到容器内的对象中。这样做的原因有很多。
  • @CodeMonkeyKing:咆哮是针对他建议的在 log4net 和他的代码之间创建抽象层的方法,尤其是问题末尾的示例代码块。
  • @JeffreyHantin 在您看来这可能是一种反模式,但我发现那里的意见与编写代码的工程师一样多。我的意见当然:)
  • 我知道这是一篇旧帖子,但您能解释一下为什么这样的包装器“不好”吗? ILog 和 LogManager 驻留在 log4net 命名空间中,通过在我的代码中使用这些类型不是将我与 log4net 耦合吗?我在这里错过了什么?
  • 包装 log4net 是很好的做法。问题不在于包装库,它做得很差。
【解决方案2】:

另一种方法是像这样连接 log4net ILog 接口容器注册序列:(在下面的 ASP.NET 上下文中使用 Castle 作为示例)

container.Register(Component.For<ILog>().LifeStyle
    .PerWebRequest.UsingFactoryMethod(() => LogManager.GetLogger(
    MethodBase.GetCurrentMethod().DeclaringType)));

只要你需要它,只需构造函数注入 ILog。

【讨论】:

  • 我认为这会更好,因为您可以为正在构建的实现类找到正确的记录器:Component.For&lt;ILog&gt;().UsingFactoryMethod((kernel, model, cc) =&gt; LogManager.GetLogger(cc.Handler.ComponentModel.Implementation))
【解决方案3】:

我的做法是创建自己的接口,并让一个类实现使用 Log4Net 的接口,并注入该类。这样,您就不会绑定到 Log4Net...您只需为新的记录器创建另一个类并注入该类。

【讨论】:

    【解决方案4】:

    如果您真的担心拥有多个记录器,您可以随时声明一个全局记录器并调用它来满足您的所有记录需求。这样做的缺点是您失去了识别发出日志的类的内置功能,但您也可以将自己的自定义消息注入日志中以识别类。

    这取决于您希望日志记录的健壮程度。您也可以简单地将记录器放在主类中,并让所有未处理的异常冒泡到它进行记录。

    【讨论】:

    • 但是您肯定通常想要记录的不仅仅是异常吗?
    • 你在这里建议单例模式吗?我希望不会。
    猜你喜欢
    • 1970-01-01
    • 2018-02-22
    • 1970-01-01
    • 2021-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-25
    • 1970-01-01
    相关资源
    最近更新 更多