在C#中,我的Common.Logging记录器应该是实例成员还是静态?

Fer*_*eia 8 c# logging common.logging

查看使用Common.Logging for .NET 的项目,我注意到有些类将logger实例声明为类静态成员.例如:

public class HelloJob : IJob
{
    private static ILog _log = LogManager.GetLogger(typeof(HelloJob));

    public HelloJob()
    {
    }

    public virtual void  Execute(IJobExecutionContext context)
    {
        _log.Info(string.Format("Hello World! - {0}", System.DateTime.Now.ToString("r")));
    }
}
Run Code Online (Sandbox Code Playgroud)

在其他类中,记录器被声明为实例成员:

public class SimpleExample : IExample
{
    public virtual void Run()
    {
        ILog log = LogManager.GetLogger(typeof (SimpleExample));

        log.Info("------- Initializing ----------------------");

        // etc
    }
}    
Run Code Online (Sandbox Code Playgroud)

是否有理由偏好一种方法或另一种方法?

在哪些情况下推荐每种方法?它与线程安全有关吗?

如果我刚刚宣布一个带有静态"记录器"成员的"Logger"类并且使用了整个项目(除了我在实践中会有一个全局变量的问题),这会是一个问题吗?

Str*_*ior 9

大多数记录器都是线程安全的,并且创建它们的实例在时间和内存方面都有很小的开销.因此,真正的问题需要从编程和可维护性的角度来看是有意义的.

一方面,由于记录器在概念上与您的类相关联,而不是与类的实例相关联,因此很多人更喜欢将其保持静态.这是一个非常有效的论点.例如,如果HelloWorldJob扩展HelloJob,我认为大多数人都希望代码编写的日志消息HelloJobHelloJob类绑定,即使你有一个更具体的子类实例.能够从静态方法访问您的记录器也很好,如果它不在静态字段上,这是不可能的.

另一方面,没有理由让HelloJob负责获取自己的记录器实例.对于使用依赖注入(单元可测试性,附加可配置性和更简单的代码),有很多要说的.所以我个人建议让你的记录器由一个DI框架注入,在这种情况下需要在每个实例字段上引用它.

public class HelloJob : IJob
{
    private readonly ILog _log;

    public HelloJob(ILog log)
    {
        _log = log;
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

您的DI框架可以根据它在运行时知道的详细信息设置记录器,或者您可以在单元测试中提供假的或模拟的记录器,以确保生成预期的日志消息.请注意,即使您指的是每个实例字段,您仍可以完全自由地使用每个类(甚至是单个)实例 - 这些只是不需要成为此类的一部分的详细信息关心.

  • @ChrisSinclair:确实,实例化*详细信息*由LogManager.GetLogger处理,我喜欢Commons.Logging提供了一个通用的日志记录接口,让您可以自由切换底层框架,配置等.但是,这是一个例子工厂模式,而不是依赖注入.工厂模式比手动实例化具有许多优势,但依赖注入仍然具有额外的优势.您的DI框架仍然可以设置为每种类型重用一个记录器,并且90%的时间内开销实际上是无法估量的. (2认同)